diff --git a/firmware/psu/.ccsproject b/firmware/psu/.ccsproject new file mode 100644 index 0000000000..25e6f2f4c5 --- /dev/null +++ b/firmware/psu/.ccsproject @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/firmware/psu/.cproject b/firmware/psu/.cproject new file mode 100644 index 0000000000..36f8895432 --- /dev/null +++ b/firmware/psu/.cproject @@ -0,0 +1,271 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/firmware/psu/.gitignore b/firmware/psu/.gitignore new file mode 100644 index 0000000000..00fa3cb528 --- /dev/null +++ b/firmware/psu/.gitignore @@ -0,0 +1,11 @@ +/Debug/ +/Release/ +/src/sysbios/ +/src/makefile.libs +.xdchelp +/.config/ +/.launches + +# NOTE: this should only be ignored when "Manage the project's +# target-configuration automatically" is selected in Properties->CCS General +/targetConfigs/ diff --git a/firmware/psu/.project b/firmware/psu/.project new file mode 100644 index 0000000000..ff4e2532dc --- /dev/null +++ b/firmware/psu/.project @@ -0,0 +1,28 @@ + + + PSU + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.rtsc.xdctools.buildDefinitions.XDC.xdcNature + com.ti.ccstudio.core.ccsNature + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/firmware/psu/.settings/com.ti.ccstudio.project.core.prefs b/firmware/psu/.settings/com.ti.ccstudio.project.core.prefs new file mode 100644 index 0000000000..76e251e8bf --- /dev/null +++ b/firmware/psu/.settings/com.ti.ccstudio.project.core.prefs @@ -0,0 +1,4 @@ +ccsVersionValidationPolicy=warning +compilerVersionValidationPolicy=flexible +eclipse.preferences.version=1 +productVersionsValidationPolicy=flexible diff --git a/firmware/psu/.settings/org.eclipse.cdt.codan.core.prefs b/firmware/psu/.settings/org.eclipse.cdt.codan.core.prefs new file mode 100644 index 0000000000..f653028c53 --- /dev/null +++ b/firmware/psu/.settings/org.eclipse.cdt.codan.core.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +inEditor=false +onBuild=false diff --git a/firmware/psu/.settings/org.eclipse.cdt.debug.core.prefs b/firmware/psu/.settings/org.eclipse.cdt.debug.core.prefs new file mode 100644 index 0000000000..2adc7b1dde --- /dev/null +++ b/firmware/psu/.settings/org.eclipse.cdt.debug.core.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.cdt.debug.core.toggleBreakpointModel=com.ti.ccstudio.debug.CCSBreakpointMarker diff --git a/firmware/psu/.settings/org.eclipse.core.resources.prefs b/firmware/psu/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000000..76334066c5 --- /dev/null +++ b/firmware/psu/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,46 @@ +eclipse.preferences.version=1 +encoding//Debug/makefile=UTF-8 +encoding//Debug/objects.mk=UTF-8 +encoding//Debug/platform/oc-sdr/cfg/subdir_rules.mk=UTF-8 +encoding//Debug/platform/oc-sdr/cfg/subdir_vars.mk=UTF-8 +encoding//Debug/platform/oc-sdr/schema/subdir_rules.mk=UTF-8 +encoding//Debug/platform/oc-sdr/schema/subdir_vars.mk=UTF-8 +encoding//Debug/sources.mk=UTF-8 +encoding//Debug/src/Devices/i2c/subdir_rules.mk=UTF-8 +encoding//Debug/src/Devices/i2c/subdir_vars.mk=UTF-8 +encoding//Debug/src/Devices/ocmp_wrappers/subdir_rules.mk=UTF-8 +encoding//Debug/src/Devices/ocmp_wrappers/subdir_vars.mk=UTF-8 +encoding//Debug/src/Devices/subdir_rules.mk=UTF-8 +encoding//Debug/src/Devices/subdir_vars.mk=UTF-8 +encoding//Debug/src/Devices/uart/subdir_rules.mk=UTF-8 +encoding//Debug/src/Devices/uart/subdir_vars.mk=UTF-8 +encoding//Debug/src/comm/subdir_rules.mk=UTF-8 +encoding//Debug/src/comm/subdir_vars.mk=UTF-8 +encoding//Debug/src/devices/i2c/subdir_rules.mk=UTF-8 +encoding//Debug/src/devices/i2c/subdir_vars.mk=UTF-8 +encoding//Debug/src/devices/ocmp_wrappers/subdir_rules.mk=UTF-8 +encoding//Debug/src/devices/ocmp_wrappers/subdir_vars.mk=UTF-8 +encoding//Debug/src/devices/subdir_rules.mk=UTF-8 +encoding//Debug/src/devices/subdir_vars.mk=UTF-8 +encoding//Debug/src/devices/uart/subdir_rules.mk=UTF-8 +encoding//Debug/src/devices/uart/subdir_vars.mk=UTF-8 +encoding//Debug/src/drivers/subdir_rules.mk=UTF-8 +encoding//Debug/src/drivers/subdir_vars.mk=UTF-8 +encoding//Debug/src/helpers/subdir_rules.mk=UTF-8 +encoding//Debug/src/helpers/subdir_vars.mk=UTF-8 +encoding//Debug/src/interfaces/UART/subdir_rules.mk=UTF-8 +encoding//Debug/src/interfaces/UART/subdir_vars.mk=UTF-8 +encoding//Debug/src/post/subdir_rules.mk=UTF-8 +encoding//Debug/src/post/subdir_vars.mk=UTF-8 +encoding//Debug/src/registry/subdir_rules.mk=UTF-8 +encoding//Debug/src/registry/subdir_vars.mk=UTF-8 +encoding//Debug/src/subdir_rules.mk=UTF-8 +encoding//Debug/src/subdir_vars.mk=UTF-8 +encoding//Debug/src/subsystem/power/subdir_rules.mk=UTF-8 +encoding//Debug/src/subsystem/power/subdir_vars.mk=UTF-8 +encoding//Debug/src/sysbios/subdir_rules.mk=UTF-8 +encoding//Debug/src/sysbios/subdir_vars.mk=UTF-8 +encoding//Debug/src/utils/subdir_rules.mk=UTF-8 +encoding//Debug/src/utils/subdir_vars.mk=UTF-8 +encoding//Debug/subdir_rules.mk=UTF-8 +encoding//Debug/subdir_vars.mk=UTF-8 diff --git a/firmware/psu/common/inc/global/Framework.h b/firmware/psu/common/inc/global/Framework.h new file mode 100644 index 0000000000..274aecf1ba --- /dev/null +++ b/firmware/psu/common/inc/global/Framework.h @@ -0,0 +1,151 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#ifndef _SYS_CFG_FRAMEWORK_H +#define _SYS_CFG_FRAMEWORK_H + +#include "common/inc/global/ocmp_frame.h" +#include "common/inc/global/post_frame.h" /* Just for post code */ + +#include +#include + +#define POST_ENABLED 0 +#define POST_DISABLED 1 + +/* For enabling schema sharing between host and firmware we need to import the + * factory config and driver config to schema.c as weak attribute from + * OC_CONNECT1.C. This helps host compilation as it doesn't need to know symbol definition for the configs + * and schema sharing can be achived with limited common files. + */ +#define SCHEMA_IMPORT extern __attribute__((weak)) + +/* DriverStruct acts as a generic datatype. + * In schema we are more intreseted in the address of structure so we use this datatype DriverStruct + * to avoid the include header for the devices in the system. + */ +typedef char DriverStruct; + +/* TODO: move these to common header file */ +typedef enum DataType { + TYPE_NULL = 0, /* No data is passed (used for simple GPIO-based alerts) */ + TYPE_INT8, + TYPE_UINT8, + TYPE_INT16, + TYPE_UINT16, + TYPE_INT32, + TYPE_UINT32, + TYPE_INT64, + TYPE_UINT64, + TYPE_STR, + TYPE_BOOL, + TYPE_ENUM, + COUNT_TYPE, +} DataType; + +typedef struct Enum_Map { + int value; + const char *name; +} Enum_Map; + +typedef struct Parameter { + const char *name; + DataType type; + union { + Enum_Map *values; + size_t size; + }; +} Parameter; + +typedef bool (*CB_Command) (void *driver, void *params); + +typedef struct Command { + const char *name; + const Parameter *parameters; + const CB_Command cb_cmd; +} Command; + +typedef bool (*CB_POST) (void **params); + +typedef struct Post { + const char *name; + const CB_POST cb_postCmd; +}Post; + +// To avoid the awkward situation of not knowing how much to allocate for the return value (think +// string returns), we instead rely on the 'get' and 'set' functions to allocate and return a +// pointer to the value it wants to return via OCMP +typedef bool (*StatusGet_Cb) (void *driver, unsigned int param_id, + void *return_buf); +typedef bool (*ConfigGet_Cb) (void *driver, unsigned int param_id, + void *return_buf); +typedef bool (*ConfigSet_Cb) (void *driver, unsigned int param_id, + const void *data); + +typedef ePostCode (*CB_Probe) (void *driver, POSTData* postData); +typedef ePostCode (*CB_Init) (void *driver, const void *config, + const void *alert_token); + +typedef bool (*ssHook_Cb) (void *driver, void *return_buf); + +typedef struct Driver_fxnTable { + // TODO: These callbacks are a bit rough. They'll get the job done, but we should revisit other + // options (per-parameter callbacks for example) + StatusGet_Cb cb_get_status; + ConfigGet_Cb cb_get_config; + ConfigSet_Cb cb_set_config; + CB_Probe cb_probe; + CB_Init cb_init; +} Driver_fxnTable; + +typedef struct Driver { + const char *name; + const Parameter *status; + const Parameter *config; + const Parameter *alerts; + const Parameter *argList; + const Command *commands; + const Driver_fxnTable* fxnTable; + const Post *post; + bool payload_fmt_union; /* TODO: hack to account for OBC/Testmodule payload + being packed as a union instead of a struct */ +} Driver; + +typedef struct SSHookSet { + ssHook_Cb preInitFxn ;/* Function will run before post is executed */ + ssHook_Cb postInitFxn; /* Function will run after post is executed */ +}SSHookSet; + +typedef void (*Component_InitCb) (void); + +typedef struct Component { + const char *name; + const struct Component *components; + const Driver *driver; + void *driver_cfg; // TODO: this could be turned into a standard polymorphism struct to hold the + // driver, hw config & driver object data (like we did for GPIO) + const void *factory_config; /* Factory defaults for the device */ + const Command *commands; /* TODO: super gross hack to fit into current CLI */ + const SSHookSet *ssHookSet; + bool postDisabled; //Flag for POST execution. + void *ss; +} Component; + +/* TODO: consider moving struct into c file - only need pointer externally */ +typedef struct AlertData { + OCMPSubsystem subsystem; + uint8_t componentId; + uint8_t deviceId; +} AlertData; + +void OCMP_GenerateAlert(const AlertData *alert_data, + unsigned int alert_id, + const void *data); + +#endif /* _SYS_CFG_FRAMEWORK_H */ diff --git a/firmware/psu/common/inc/global/OC_CONNECT1.h b/firmware/psu/common/inc/global/OC_CONNECT1.h new file mode 100644 index 0000000000..5fcae7f2fa --- /dev/null +++ b/firmware/psu/common/inc/global/OC_CONNECT1.h @@ -0,0 +1,237 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef __OC_CONNECT1_H +#define __OC_CONNECT1_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define OC_PMIC_ENABLE (1) +#define OC_PMIC_DISABLE (0) +#define OC_SDR_ENABLE (1) +#define OC_SDR_DISABLE (0) +#define OC_SDR_FE_IO_ENABLE (1) +#define OC_SDR_FE_IO_DISABLE (0) +#define OC_FE_ENABLE (1) +#define OC_FE_DISABLE (0) +#define OC_PWR_LION_BATT (1) +#define OC_PWR_LEAD_BATT (0) +#define OC_PWR_PSE_RESET_STATE (1) +#define OC_PWR_PSE_ON_STATE (0) +#define OC_GBC_PROC_ENABLE (1) +#define OC_GBC_PROC_RESET (0) +#define OC_SYNC_IOEXP_ENABLE (1) +#define OC_SYNC_IOEXP_RESET (0) +#define OC_HCI_LED_ENABLE (1) +#define OC_HCI_LED_DISABLE (0) +#define OC_ETH_SW_ENABLE (1) +#define OC_ETH_SW_DISABLE (0) +#define CAT24C256 { .page_size = 64, .mem_size = (256 / 8) } + +/* GBC IO expander Slave address */ +#define BIGBROTHER_IOEXP0_ADDRESS 0x71 +#define BIGBROTHER_IOEXP1_ADDRESS 0x70 +/* SYNC IO expander Slave address */ +#define SYNC_IO_DEVICE_ADDR 0x71 +/* SDR IO expander Slave address */ +#define SDR_FX3_IOEXP_ADDRESS 0x1E +/* RFFE IO expander Slave address */ +#define RFFE_CHANNEL1_IO_TX_ATTEN_ADDR 0x18 +#define RFFE_CHANNEL1_IO_RX_ATTEN_ADDR 0x1A +#define RFFE_CHANNEL2_IO_TX_ATTEN_ADDR 0x1C +#define RFFE_CHANNEL2_IO_RX_ATTEN_ADDR 0x1D +#define RFFE_IO_REVPOWER_ALERT_ADDR 0x1B + +/*! + * @def OC_CONNECT1_EMACName + * @brief Enum of EMAC names on the OC_CONNECT1 board + */ +typedef enum OC_CONNECT1_EMACName { + OC_CONNECT1_EMAC0 = 0, + + OC_CONNECT1_EMACCOUNT +} OC_CONNECT1_EMACName; + +/*! + * @def OC_CONNECT1_GPIOName + * @brief Enum of GPIO names on the OC_CONNECT1 board + */ +typedef enum OC_EC_PORTGroupName { + PA = 1, + PB, + PC, + PD, + PE, + PF, + PG +}OC_EC_PORTGroupName; + +typedef enum OC_CONNECT1_GPIOName { + //PA + OC_EC_PSE_INT = 0, + OC_EC_nPSE_RESET, + OC_EC_WD_INPUT, + OC_EC_ENABLE_OC_INPUT, + OC_EC_POWER_OFF, + OC_EC_NOC_PA5, + OC_EC_CS_I2C1_SCLK, + OC_EC_CS_I2C1_SDA, + //PB + OC_EC_DISABLE_DC_INPUT = 8, + OC_EC_IOEXP_INT, + OC_EC_CS_I2C0_SCLK, + OC_EC_CS_I2C0_SDA, + OC_EC_ENABLE_PASSIVE_POE, + OC_EC_CS_ALERT_24V_20V, + OC_EC_TEMP_I2C5_SCL, + OC_EC_TEMP_I2C5_SDA, + //PC + OC_EC_JTAG_TCK = 16, + OC_EC_JTAG_TMS, + OC_EC_JTAG_TDI, + OC_EC_JTAG_TDO, + OC_EC_UART_RX, + OC_EC_EN_INT_BATT_PWR, + OC_EC_DC_INPUT_FAULT, + OC_EC_UART_TX, + //PD + OC_EC_CS_ALERT = 24, + OC_EC_DC_IN_PRESENT, + OC_EC_CS_ALERT_12V_GBC, + OC_EC_CS_ALERT_12V_BB, + OC_EC_CS_ALERT_12V_FE, + OC_EC_PGOOD_5V0, + OC_EC_PGOOD_12V0, + OC_NOC_PD7, + //PE + OC_EC_IVINMON = 32, + OC_EC_ISMON,//OC_CONNECT1_GBC_TEMP_ALERT2, + OC_EC_CHARGER_ALERT, + OC_EC_PGOOD_BOOST_CONV_BATT, + OC_EC_CHARGER_I2C2_SCL, + OC_EC_CHARGER_I2C2_SDA, + //PF + OC_EC_TIVA_GPIO1 = 40, + OC_EC_TIVA_GPIO2, + OC_EC_BUZZER_ON, + OC_EC_PD_T2P, + OC_EC_TEMP_EVENT, + //PG + OC_EC_PB_PSE_I2C3_SCL = 48, + OC_EC_PB_PSE_I2C3_SDA, + OC_EC_CS_I2C4_SCL, + OC_EC_CS_I2C4_SDA, + OC_EC_OC_IN_PRESENT, + OC_EC_POE_IN_PRESENT, + OC_EC_GPIOCOUNT +} OC_CONNECT1_GPIOName; + +/*! + * @def OC_CONNECT1_I2CName + * @brief Enum of I2C names on the OC_CONNECT1 board + */ +typedef enum OC_CONNECT1_I2CName { + OC_CONNECT1_I2C0 = 0, + OC_CONNECT1_I2C1, + OC_CONNECT1_I2C2, + OC_CONNECT1_I2C3, + OC_CONNECT1_I2C4, + OC_CONNECT1_I2C5, + OC_CONNECT1_I2CCOUNT +} OC_CONNECT1_I2CName; + +/*! + * @def OC_CONNECT1_UARTName + * @brief Enum of UARTs on the OC_CONNECT1 board + */ +typedef enum OC_CONNECT1_UARTName { + OC_CONNECT1_UART3, + OC_CONNECT1_UART4, + OC_CONNECT1_UARTCOUNT +} OC_CONNECT1_UARTName; + +/* + * @def OC_CONNECT1_WatchdogName + * @brief Enum of Watchdogs on the OC_CONNECT1 board + */ +typedef enum OC_CONNECT1_WatchdogName { + OC_CONNECT1_WATCHDOG0 = 0, + OC_CONNECT1_WATCHDOGCOUNT +} OC_CONNECT1_WatchdogName; + +/*! + * @brief Initialize the general board specific settings + * + * This function initializes the general board specific settings. + * This includes: + * - Enable clock sources for peripherals + */ +void OC_CONNECT1_initGeneral(void); + +/*! + * @brief Initialize board specific EMAC settings + * + * This function initializes the board specific EMAC settings and + * then calls the EMAC_init API to initialize the EMAC module. + * + * The EMAC address is programmed as part of this call. + * + */ +void OC_CONNECT1_initEMAC(void); + +/*! + * @brief Initialize board specific GPIO settings + * + * This function initializes the board specific GPIO settings and + * then calls the GPIO_init API to initialize the GPIO module. + * + * The GPIOs controlled by the GPIO module are determined by the GPIO_PinConfig + * variable. + */ +void OC_CONNECT1_initGPIO(void); + +/*! + * @brief Initialize board specific I2C settings + * + * This function initializes the board specific I2C settings and then calls + * the I2C_init API to initialize the I2C module. + * + * The I2C peripherals controlled by the I2C module are determined by the + * I2C_config variable. + */ +void OC_CONNECT1_initI2C(void); + +/*! + * @brief Initialize board specific UART settings + * + * This function initializes the board specific UART settings and then calls + * the UART_init API to initialize the UART module. + * + * The UART peripherals controlled by the UART module are determined by the + * UART_config variable. + */ +void OC_CONNECT1_initUART(void); + +/*! + * @brief Initialize board specific Watchdog settings + * + * This function initializes the board specific Watchdog settings and then + * calls the Watchdog_init API to initialize the Watchdog module. + * + * The Watchdog peripherals controlled by the Watchdog module are determined + * by the Watchdog_config variable. + */ +void OC_CONNECT1_initWatchdog(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __OC_CONNECT1_H */ diff --git a/firmware/psu/common/inc/global/ocmp_frame.h b/firmware/psu/common/inc/global/ocmp_frame.h new file mode 100644 index 0000000000..7918e3dc02 --- /dev/null +++ b/firmware/psu/common/inc/global/ocmp_frame.h @@ -0,0 +1,166 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef OCMP_FRAME_H_ +#define OCMP_FRAME_H_ + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include + +/***************************************************************************** + * MACRO DEFINITIONS + *****************************************************************************/ +/* Start Of Frame & Message Lengths */ +#define OCMP_MSG_SOF 0x55 +#define OCMP_FRAME_TOTAL_LENGTH 64 +#define OCMP_FRAME_HEADER_LENGTH 17 +#define OCMP_FRAME_MSG_LENGTH (OCMP_FRAME_TOTAL_LENGTH - OCMP_FRAME_HEADER_LENGTH) + +/***************************************************************************** + * STRUCT/ENUM DEFINITIONS + *****************************************************************************/ + +typedef enum { + OC_SS_BB = -1, //Hack around the fact that IPC reuses OCMP to allow us + // to split BB (internal) and SYS (CLI) message handling + OC_SS_PWR = 0, + // OC_SS_PWRBMS, + OC_SS_PWRDEBUG, + OC_SS_MAX_LIMIT,//TODO:REV C Change +} OCMPSubsystem; + +typedef enum { + OCMP_COMM_IFACE_UART = 1, // Uart - 1 + OCMP_COMM_IFACE_ETHERNET, // Ethernet - 2 + OCMP_COMM_IFACE_SBD, // SBD(Satellite) - 3 + OCMP_COMM_IFACE_USB // Usb - 4 +} OCMPInterface; + +/* + * OCMPMsgType - msg type specifies what is the communication all about. + * It can be Configuration, Status, Alert, Command, Watchdog, Debug + * OCMPMsgType 1 byte message. + * ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + * || 7 || 6 || 5 || 4 || 3 || 2 || 1 || 0 || + * ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + || Message Type || + * ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + */ +typedef enum { + OCMP_MSG_TYPE_CONFIG = 1, + OCMP_MSG_TYPE_STATUS, + OCMP_MSG_TYPE_ALERT, + OCMP_MSG_TYPE_COMMAND, + OCMP_MSG_TYPE_WATCHDOG, + OCMP_MSG_TYPE_DEBUG, + OCMP_MSG_TYPE_EVENTINFO, + OCMP_MSG_TYPE_ITCMSG, + OCMP_MSG_TYPE_POST, +} OCMPMsgType; + +/* + * OCMPActionType - It is about setting something or getting or Reply back. + */ +typedef enum { + OCMP_AXN_TYPE_GET = 1, + OCMP_AXN_TYPE_SET, + OCMP_AXN_TYPE_REPLY, + OCMP_AXN_TYPE_ACTIVE, + OCMP_AXN_TYPE_CLEAR, + OCMP_AXN_TYPE_RESET, + OCMP_AXN_TYPE_ENABLE, + OCMP_AXN_TYPE_DISABLE, + OCMP_AXN_REG_READ, + OCMP_AXN_REG_WRITE, + OCMP_AXN_TYPE_ECHO, + + /* TODO: Really shouldn't be generic commands, but keeping here for now + * because refactoring would be too painful just to test the CLI + */ + OCMP_AXN_DIS_NETWORK, + OCMP_AXN_CONN_NETWORK, + OCMP_AXN_SEND_SMS, + OCMP_AXN_DIAL_NUMBER, + OCMP_AXN_ANSWER, + OCMP_AXN_HANGUP, + OCMP_NUM_ACTIONS +} OCMPActionType; + +typedef enum { + OCMP_SENSOR_EVENT = 1, + OCMP_TEMP_EVENT, + OCMP_CURRENT_EVENT, + OCMP_REV_POWER_EVENT, + OCMP_BATT_EVENT, + OCMP_PSE_EVENT, + OCMP_PD_EVENT, + OCMP_ETH_EVENT, + OCMP_GPS_EVENT +} OCMPEvents; + +/* + * eOCMPDebugOperation - This forms the complete debug message frame for + * communication with EC from External entity (e.g. AP over UART, Ethernet + * or SBD) + */ +typedef enum { + OCMP_DEBUG_READ = 1, + OCMP_DEBUG_WRITE +} eOCMPDebugOperation; + +/* TODO::This OCWARE_HOST has to be removed with OCMP cleanUp*/ +#ifndef OCWARE_HOST + #define OC_SS OCMPSubsystem + #define OC_MSG_TYP OCMPMsgType + #define OC_AXN_TYP OCMPActionType +#else + #define OC_SS uint8_t + #define OC_MSG_TYP uint8_t + #define OC_AXN_TYP uint8_t + #define OC_IFACE_TYP uint8_t +#endif +/* + * Header is the field which will be containing SOF, Framelen, + * Source Interface, Sequence number, and timestamp. + */ +typedef struct __attribute__((packed, aligned(1))) { + uint8_t ocmpSof; // SOF - It must be 0x55 + uint8_t ocmpFrameLen; // Framelen - tells about the configuration size ONLY. + OCMPInterface ocmpInterface; // Interface - UART/Ethernet/SBD + uint32_t ocmpSeqNumber; // SeqNo - Don't know!!! + uint32_t ocmpTimestamp; // Timestamp - When AP sent the command? +} OCMPHeader; + +/* + * This is the Message structure for Subsystem level information + */ +typedef struct __attribute__((packed, aligned(1))) { + OC_SS subsystem; // RF/GPP/BMS/Watchdog etc.. + uint8_t componentID; // Compononent ID. Different for different subsystem. + OCMPMsgType msgtype; // Msg type is Config/Status/Alert/Command/Watchdog/Debug + uint8_t action; // Action is - Get/Set/Reply. + uint16_t parameters; // List of Parameters to be set or get. +#ifndef OCWARE_HOST + uint8_t ocmp_data[]; // The data payload. +#else + int8_t* info; +#endif +} OCMPMessage; + +/* + * OCMPMessageFrame - This forms the complete message frame for communication + * with EC from External entity (e.g. AP over UART, Ethernet or SBD) + */ +typedef struct __attribute__((packed, aligned(1))) { + OCMPHeader header; + OCMPMessage message; +} OCMPMessageFrame; + +#endif /* OCMP_FRAME_H_ */ diff --git a/firmware/psu/common/inc/global/post_frame.h b/firmware/psu/common/inc/global/post_frame.h new file mode 100644 index 0000000000..d904208161 --- /dev/null +++ b/firmware/psu/common/inc/global/post_frame.h @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef POST_FRAME_H_ +#define POST_FRAME_H_ + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include + +/***************************************************************************** + * STRUCT/ENUM DEFINITIONS + *****************************************************************************/ +typedef enum { + POST_DEV_NOSTATUS = 0, + POST_DEV_MISSING, + POST_DEV_ID_MISMATCH, + POST_DEV_FOUND, + POST_DEV_CFG_DONE, + POST_DEV_NO_CFG_REQ, + POST_DEV_CFG_FAIL, + POST_DEV_FAULTY, + POST_DEV_CRITICAL_FAULT, + POST_DEV_NO_DRIVER_EXIST, +} ePostCode; + +typedef struct __attribute__((packed, aligned(1))) { + uint8_t subsystem; + uint8_t devSno; + uint8_t i2cBus; + uint8_t devAddr; + uint16_t devId; + uint16_t manId; + uint8_t status; +} POSTData; + +#endif /* POST_FRAME_H_ */ diff --git a/firmware/psu/common/inc/ocmp_wrappers/ocmp_debugi2c.h b/firmware/psu/common/inc/ocmp_wrappers/ocmp_debugi2c.h new file mode 100644 index 0000000000..01c4005721 --- /dev/null +++ b/firmware/psu/common/inc/ocmp_wrappers/ocmp_debugi2c.h @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef OCMP_I2C_H_ +#define OCMP_I2C_H_ + +#include "common/inc/global/Framework.h" + +SCHEMA_IMPORT bool i2c_read(void *driver, void *data); +SCHEMA_IMPORT bool i2c_write(void *driver, void *data); + +static const Driver OC_I2C = { + .name = "OC_I2C", + .argList = (Parameter[]){ + { .name = "slave_address", .type = TYPE_UINT8 }, + { .name = "no_of_bytes", .type = TYPE_UINT8 }, + { .name = "reg_address", .type = TYPE_UINT8 }, + { .name = "reg_values", .type = TYPE_UINT16 }, + {} + }, + .commands = (Command[]){ + { + .name = "get", + .cb_cmd = i2c_read, + }, + { + .name = "set", + .cb_cmd = i2c_write, + }, + {} + }, +}; + +#endif /* INC_DEVICES_OCMP_WRAPPERS_OCMP_I2C_H_ */ diff --git a/firmware/psu/common/inc/ocmp_wrappers/ocmp_debugocgpio.h b/firmware/psu/common/inc/ocmp_wrappers/ocmp_debugocgpio.h new file mode 100644 index 0000000000..10dd21eafb --- /dev/null +++ b/firmware/psu/common/inc/ocmp_wrappers/ocmp_debugocgpio.h @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef OCMP_OCGPIO_H_ +#define OCMP_OCGPIO_H_ + +#include "common/inc/global/Framework.h" + +SCHEMA_IMPORT bool ocgpio_get(void *driver, void *data); +SCHEMA_IMPORT bool ocgpio_set(void *driver, void *data); + +SCHEMA_IMPORT const Driver_fxnTable DEBUG_OCGPIO_fxnTable; + +static const Driver OC_GPIO = { + .name = "OC_GPIO", + .argList = (Parameter[]){ + { .name = "pin", .type = TYPE_UINT8 }, + { .name = "value", .type = TYPE_UINT8 }, + {} + }, + .commands = (Command[]){ + { + .name = "get", + .cb_cmd = ocgpio_get, + }, + { + .name = "set", + .cb_cmd = ocgpio_set, + }, + {} + }, + .fxnTable = &DEBUG_OCGPIO_fxnTable, +}; + +#endif /* OCMP_OCGPIO_H_ */ diff --git a/firmware/psu/common/inc/ocmp_wrappers/ocmp_eeprom_cat24c04.h b/firmware/psu/common/inc/ocmp_wrappers/ocmp_eeprom_cat24c04.h new file mode 100644 index 0000000000..3288c225f5 --- /dev/null +++ b/firmware/psu/common/inc/ocmp_wrappers/ocmp_eeprom_cat24c04.h @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef INC_DEVICES_OCMP_EEPROM_H_ +#define INC_DEVICES_OCMP_EEPROM_H_ + +#include "common/inc/global/Framework.h" + +SCHEMA_IMPORT const Driver_fxnTable CAT24C04_psu_sid_fxnTable; +SCHEMA_IMPORT const Driver_fxnTable CAT24C04_psu_inv_fxnTable; + +static const Driver CAT24C04_psu_sid = { + .name = "EEPROM", + .status = (Parameter[]){ + { .name = "ocserialinfo", .type = TYPE_STR, .size = 21 }, + { .name = "gbcboardinfo", .type = TYPE_STR, .size = 21 }, + }, + .fxnTable = &CAT24C04_psu_sid_fxnTable, +}; + +static const Driver CAT24C04_psu_inv = { + .name = "Inventory", + .fxnTable = &CAT24C04_psu_inv_fxnTable, +}; + +#endif /* INC_DEVICES_OCMP_EEPROM_H_ */ diff --git a/firmware/psu/common/inc/ocmp_wrappers/ocmp_ina226.h b/firmware/psu/common/inc/ocmp_wrappers/ocmp_ina226.h new file mode 100644 index 0000000000..6b554f0852 --- /dev/null +++ b/firmware/psu/common/inc/ocmp_wrappers/ocmp_ina226.h @@ -0,0 +1,40 @@ +/** +* Copyright (c) 2017-present, Facebook, Inc. +* All rights reserved. +* +* This source code is licensed under the BSD-style license found in the +* LICENSE file in the root directory of this source tree. An additional grant +* of patent rights can be found in the PATENTS file in the same directory. +*/ +#ifndef _OCMP_INA226_H +#define _OCMP_INA226_H + +#include "common/inc/global/Framework.h" + +typedef struct INA226_Config { + uint16_t current_lim; +} INA226_Config; + +SCHEMA_IMPORT const Driver_fxnTable INA226_fxnTable; + +static const Driver INA226 = { + .name = "INA226", + .status = (Parameter[]){ + { .name = "busvoltage", .type = TYPE_UINT16 }, + { .name = "shuntvoltage", .type = TYPE_UINT16 }, + { .name = "current", .type = TYPE_UINT16 }, + { .name = "power", .type = TYPE_UINT16 }, + {} + }, + .config = (Parameter[]){ + { .name = "currlimit", .type = TYPE_UINT16 }, + {} + }, + .alerts = (Parameter[]){ + { .name = "Overcurrent", .type = TYPE_UINT16 }, + {} + }, + .fxnTable = &INA226_fxnTable, +}; + +#endif /* _OCMP_INA226_H */ diff --git a/firmware/psu/common/inc/ocmp_wrappers/ocmp_ltc4015.h b/firmware/psu/common/inc/ocmp_wrappers/ocmp_ltc4015.h new file mode 100644 index 0000000000..b1b935ae41 --- /dev/null +++ b/firmware/psu/common/inc/ocmp_wrappers/ocmp_ltc4015.h @@ -0,0 +1,63 @@ +/** +* Copyright (c) 2017-present, Facebook, Inc. +* All rights reserved. +* +* This source code is licensed under the BSD-style license found in the +* LICENSE file in the root directory of this source tree. An additional grant +* of patent rights can be found in the PATENTS file in the same directory. +*/ +#ifndef _OCMP_LTC4015_H +#define _OCMP_LTC4015_H + +#include "common/inc/global/Framework.h" + +typedef struct LTC4015_Config { + int16_t batteryVoltageLow; + int16_t batteryVoltageHigh; + int16_t batteryCurrentLow; + int16_t inputVoltageLow; + int16_t inputCurrentHigh; + uint16_t inputCurrentLimit; + uint16_t icharge; + uint16_t vcharge; +} LTC4015_Config; + +SCHEMA_IMPORT const Driver_fxnTable LTC4015_fxnTable; + +static const Driver LTC4015 = { + .name = "LTC4015", + .status = (Parameter[]){ + { .name = "batteryVoltage", .type = TYPE_INT16 }, + { .name = "batteryCurrent", .type = TYPE_INT16 }, + { .name = "systemVoltage", .type = TYPE_INT16 }, + { .name = "inputVoltage", .type = TYPE_INT16 }, + { .name = "inputCurrent", .type = TYPE_INT16 }, + { .name = "dieTemperature", .type = TYPE_INT16 }, + { .name = "ichargeDAC", .type = TYPE_INT16 }, + {} + }, + .config = (Parameter[]){ + { .name = "batteryVoltageLow", .type = TYPE_INT16 }, + { .name = "batteryVoltageHigh", .type = TYPE_INT16 }, + { .name = "batteryCurrentLow", .type = TYPE_INT16 }, + { .name = "inputVoltageLow", .type = TYPE_INT16 }, + { .name = "inputCurrentHigh", .type = TYPE_INT16 }, + { .name = "inputCurrentLimit", .type = TYPE_UINT16 }, + { .name = "icharge", .type = TYPE_UINT16 }, + { .name = "vcharge", .type = TYPE_UINT16 }, + { .name = "dieTemperature", .type = TYPE_INT16 }, + {} + }, + .alerts = (Parameter[]){ + { .name = "BVL", .type = TYPE_INT16 }, + { .name = "BVH", .type = TYPE_INT16 }, + { .name = "BCL", .type = TYPE_INT16 }, + { .name = "IVL", .type = TYPE_INT16 }, + { .name = "ICH", .type = TYPE_INT16 }, + { .name = "DTH", .type = TYPE_INT16 }, + {} + }, + .fxnTable = <C4015_fxnTable, +}; + +#endif /* _OCMP_LTC4015_H */ diff --git a/firmware/psu/common/inc/ocmp_wrappers/ocmp_ltc4274.h b/firmware/psu/common/inc/ocmp_wrappers/ocmp_ltc4274.h new file mode 100644 index 0000000000..ce762b56aa --- /dev/null +++ b/firmware/psu/common/inc/ocmp_wrappers/ocmp_ltc4274.h @@ -0,0 +1,69 @@ +/** +* Copyright (c) 2017-present, Facebook, Inc. +* All rights reserved. +* +* This source code is licensed under the BSD-style license found in the +* LICENSE file in the root directory of this source tree. An additional grant +* of patent rights can be found in the PATENTS file in the same directory. +*/ +#ifndef _OCMP_LTC4274_H_ +#define _OCMP_LTC4274_H_ + +#include "common/inc/global/Framework.h" + +typedef union LTC4274_Config { + struct { + int8_t operatingMode; + int8_t detectEnable; + int8_t interruptMask; + bool interruptEnable; + int8_t pseHpEnable; + }; +} LTC4274_Config; + +#ifdef UT_FRAMEWORK +extern const Driver_fxnTable LTC4274_fxnTable; +#else +SCHEMA_IMPORT const Driver_fxnTable LTC4274_fxnTable; +#endif +SCHEMA_IMPORT bool LTC4274_reset(void *driver, void *params); + +static const Driver LTC4274 = { + .name = "PSE", + .status = (Parameter[]){ + { .name = "detection", .type = TYPE_UINT16 }, + { .name = "class", .type = TYPE_UINT16 }, + { .name = "powerGood", .type = TYPE_UINT16 }, + {} + }, + .config = (Parameter[]){ + { .name = "operatingMode", .type = TYPE_UINT16 }, + { .name = "detectEnable", .type = TYPE_UINT16 }, + { .name = "interruptMask", .type = TYPE_UINT16 }, + { .name = "interruptEnable", .type = TYPE_UINT16 }, + { .name = "enableHighpower", .type = TYPE_UINT16 }, + {} + }, + .alerts = (Parameter[]){ + { .name = "NoAlert", .type = TYPE_UINT8 }, + { .name = "PowerEnable", .type = TYPE_UINT8 }, + { .name = "PowerGood", .type = TYPE_UINT8 }, + { .name = "DiconnectAlert", .type = TYPE_UINT8 }, + { .name = "DetectionAlert", .type = TYPE_UINT8 }, + { .name = "ClassAlert", .type = TYPE_UINT8 }, + { .name = "TCUTAler", .type = TYPE_UINT8 }, + { .name = "TStartAlert", .type = TYPE_UINT8 }, + { .name = "SupplyAlert", .type = TYPE_UINT8 }, + {} + }, + .commands = (Command[]){ + { + .name = "reset", + .cb_cmd = LTC4274_reset, + }, + {} + }, + .fxnTable = <C4274_fxnTable, +}; + +#endif /* _OCMP_LTC4274_H_ */ diff --git a/firmware/psu/common/inc/ocmp_wrappers/ocmp_ltc4295.h b/firmware/psu/common/inc/ocmp_wrappers/ocmp_ltc4295.h new file mode 100644 index 0000000000..5d03c3f33b --- /dev/null +++ b/firmware/psu/common/inc/ocmp_wrappers/ocmp_ltc4295.h @@ -0,0 +1,32 @@ +/** +* Copyright (c) 2017-present, Facebook, Inc. +* All rights reserved. +* +* This source code is licensed under the BSD-style license found in the +* LICENSE file in the root directory of this source tree. An additional grant +* of patent rights can be found in the PATENTS file in the same directory. +*/ +#ifndef COMMON_INC_OCMP_WRAPPERS_OCMP_LTC4295_H_ +#define COMMON_INC_OCMP_WRAPPERS_OCMP_LTC4295_H_ + +#include "common/inc/global/Framework.h" + +SCHEMA_IMPORT const Driver_fxnTable LTC4295_fxnTable; + +static const Driver LTC4295 = { + .name = "LTC4295", + .status = (Parameter[]){ + { .name = "class", .type = TYPE_ENUM }, + { .name = "powerGoodState", .type = TYPE_ENUM }, + {} + }, + .alerts = (Parameter[]){ + { .name = "INCOMPATIBLE", .type = TYPE_ENUM }, + { .name = "DISCONNECT", .type = TYPE_ENUM }, + { .name = "CONNECT", .type = TYPE_ENUM }, + {} + }, + .fxnTable = <C4295_fxnTable, +}; + +#endif /* COMMON_INC_OCMP_WRAPPERS_OCMP_LTC4295_H_ */ diff --git a/firmware/psu/common/inc/ocmp_wrappers/ocmp_powersource.h b/firmware/psu/common/inc/ocmp_wrappers/ocmp_powersource.h new file mode 100644 index 0000000000..d4b2cafe8e --- /dev/null +++ b/firmware/psu/common/inc/ocmp_wrappers/ocmp_powersource.h @@ -0,0 +1,49 @@ +/** +* Copyright (c) 2017-present, Facebook, Inc. +* All rights reserved. +* +* This source code is licensed under the BSD-style license found in the +* LICENSE file in the root directory of this source tree. An additional grant +* of patent rights can be found in the PATENTS file in the same directory. +*/ +#ifndef _OCMP_POWERSOURCE_H_ +#define _OCMP_POWERSOURCE_H_ + +#include "common/inc/global/Framework.h" + +SCHEMA_IMPORT bool PWR_post_get_results(void **getpostResult); +SCHEMA_IMPORT bool PWR_post_enable(void **postActivate); +SCHEMA_IMPORT const Driver_fxnTable PWRSRC_fxnTable; + +static const Driver PWRSRC = { + .name = "powerSource", + .status = + (Parameter[]){ + { .name = "extPowerAvailability", .type = TYPE_UINT8 }, + { .name = "extPowerAccessebility", .type = TYPE_UINT8 }, + { .name = "poeAvailability", .type = TYPE_UINT8 }, + { .name = "poeAccessebility", .type = TYPE_UINT8 }, + { .name = "battAvailability", .type = TYPE_UINT8 }, + { .name = "battAccessebility", .type = TYPE_UINT8 }, + {} + }, + .config = (Parameter[]){ + {} + }, + .alerts = (Parameter[]){ + {} + }, + .post = (Post[]){ + { + .name = "results", + .cb_postCmd = PWR_post_get_results, + }, + { + .name = "enable", + .cb_postCmd = PWR_post_enable, + }, + {} + }, + .fxnTable = &PWRSRC_fxnTable, +}; +#endif /* _OCMP_POWERSOURCE_H_ */ diff --git a/firmware/psu/common/inc/ocmp_wrappers/ocmp_se98a.h b/firmware/psu/common/inc/ocmp_wrappers/ocmp_se98a.h new file mode 100644 index 0000000000..ae2ce80d0f --- /dev/null +++ b/firmware/psu/common/inc/ocmp_wrappers/ocmp_se98a.h @@ -0,0 +1,46 @@ +/** +* Copyright (c) 2017-present, Facebook, Inc. +* All rights reserved. +* +* This source code is licensed under the BSD-style license found in the +* LICENSE file in the root directory of this source tree. An additional grant +* of patent rights can be found in the PATENTS file in the same directory. +*/ +#ifndef _OCMP_SE98A_H +#define _OCMP_SE98A_H + +#include "common/inc/global/Framework.h" + +typedef union SE98A_Config { + struct { + int8_t lowlimit; + int8_t highlimit; + int8_t critlimit; + }; + int8_t limits[3]; +} SE98A_Config; + +SCHEMA_IMPORT const Driver_fxnTable SE98_fxnTable; + +static const Driver SE98A = { + .name = "SE98A", + .status = (Parameter[]){ + { .name = "temperature", .type = TYPE_UINT8 }, + {} + }, + .config = (Parameter[]){ + { .name = "lowlimit", .type = TYPE_INT8 }, + { .name = "highlimit", .type = TYPE_UINT8 }, + { .name = "critlimit", .type = TYPE_UINT8 }, + {} + }, + .alerts = (Parameter[]){ + { .name = "BAW", .type = TYPE_UINT8 }, + { .name = "AAW", .type = TYPE_UINT8 }, + { .name = "ACW", .type = TYPE_UINT8 }, + {} + }, + .fxnTable = &SE98_fxnTable, +}; + +#endif /* _OCMP_SE98A_H */ diff --git a/firmware/psu/inc/common/bigbrother.h b/firmware/psu/inc/common/bigbrother.h new file mode 100644 index 0000000000..4d6b899ea7 --- /dev/null +++ b/firmware/psu/inc/common/bigbrother.h @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#ifndef BIGBROTHER_H_ +#define BIGBROTHER_H_ + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include "inc/common/global_header.h" +#include "inc/utils/util.h" + +/***************************************************************************** + * MACRO DEFINITIONS + *****************************************************************************/ +#define BIGBROTHER_TASK_PRIORITY 5 +#define BIGBROTHER_TASK_STACK_SIZE 2048 + +/* Semaphore and Queue Handles for Big Brother */ +extern Semaphore_Handle semBigBrotherMsg; +extern Queue_Handle bigBrotherRxMsgQueue; +extern Queue_Handle bigBrotherTxMsgQueue; + +/***************************************************************************** + * FUNCTION DECLARATIONS + *****************************************************************************/ +void bigbrother_createtask(void); + +#endif /* BIGBROTHER_H_ */ diff --git a/firmware/psu/inc/common/byteorder.h b/firmware/psu/inc/common/byteorder.h new file mode 100644 index 0000000000..2d98269d1b --- /dev/null +++ b/firmware/psu/inc/common/byteorder.h @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#ifndef INC_COMMON_BYTEORDER_H_ +#define INC_COMMON_BYTEORDER_H_ + +/* Detect endianness if using TI compiler */ +#ifndef __BYTE_ORDER__ + #define __ORDER_LITTLE_ENDIAN__ 1234 + #define __ORDER_BIG_ENDIAN__ 4321 + #ifdef __little_endian__ + #define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__ + #else + #ifdef __big_endian__ + #define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__ + #else + #error Unable to detect byte order! + #endif + #endif +#endif + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + /* Little endian host functions here */ + #define htobe16(a) ( (((a)>>8)&0xff) + (((a)<<8)&0xff00) ) + #define betoh16(a) htobe16(a) + + #define htobe32(a) ((((a) & 0xff000000) >> 24) | (((a) & 0x00ff0000) >> 8) | \ + (((a) & 0x0000ff00) << 8) | (((a) & 0x000000ff) << 24) ) + #define betoh32(a) htobe32(a) + + #define htole16(a) a; // Host is a little endian. + #define letoh16(a) htole16(a) + +#else + /* Big endian host functions here */ + #define htole16(a) ( (((a)>>8)&0xff) + (((a)<<8)&0xff00) ) + #define letoh16(a) htobe16(a) + + #define htole32(a) ((((a) & 0xff000000) >> 24) | (((a) & 0x00ff0000) >> 8) | \ + (((a) & 0x0000ff00) << 8) | (((a) & 0x000000ff) << 24) ) + #define letoh32(a) htobe32(a) + + #define htobe16(a) a; // Host is a little endian. + #define betoh16(a) htole16(a) + +#endif + + +#endif /* INC_COMMON_BYTEORDER_H_ */ diff --git a/firmware/psu/inc/common/global_header.h b/firmware/psu/inc/common/global_header.h new file mode 100644 index 0000000000..7afa760bc1 --- /dev/null +++ b/firmware/psu/inc/common/global_header.h @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef GLOBAL_HEADER_H_ +#define GLOBAL_HEADER_H_ + +#define _FW_REV_MAJOR_ 0 +#define _FW_REV_MINOR_ 4 +#define _FW_REV_BUGFIX_ 0 +#define _FW_REV_TAG_ __COMMIT_HASH__ + +/* xdc/runtime/System.h is poorly written so this must be included first */ +#include + +/* XDCtools Header files */ +#include /* For System_printf */ + +#if 1 +#define DEBUG(...) {System_printf(__VA_ARGS__); System_flush();} + +#define LOGGER(...) {System_printf(__VA_ARGS__); System_flush();} +#define LOGGER_WARNING(...) {System_printf(__VA_ARGS__); System_flush();} +#define LOGGER_ERROR(...) {System_printf(__VA_ARGS__); System_flush();} +#ifdef DEBUG_LOGS +#define LOGGER_DEBUG(...) {System_printf(__VA_ARGS__); System_flush();} + +#define NOP_DELAY() { uint32_t delay =7000000;\ + while (delay--) \ + ;\ + } +#else + #define LOGGER_DEBUG(...) + #define NOP_DELAY() +#endif +#else +#define DEBUG(...) // + +#define LOGGER(...) // +#define LOGGER_WARNING(...) // +#define LOGGER_ERROR(...) // +#ifdef DEBUG_LOGS +#define LOGGER_DEBUG(...) // +#endif +#define NOP_DELAY() { uint32_t delay =7000000;\ + while (delay--) \ + ;\ + } +#endif +#define RET_OK 0 +#define RET_NOT_OK 1 + +typedef enum { + RETURN_OK = 0x00, + RETURN_NOTOK = 0x01, + RETURN_OCMP_INVALID_SS_TYPE = 0x02, + RETURN_OCMP_INVALID_MSG_TYPE = 0x03, + RETURN_OCMP_INVALID_COMP_TYPE = 0x04, + RETURN_OCMP_INVALID_AXN_TYPE = 0x05, + RETURN_OCMP_INVALID_PARAM_INFO = 0x06, + RETURN_OCMP_INVALID_CMD_INFO = 0x07, + RETURN_OCMP_INVALID_IFACE_TYPE = 0x08, + RETURN_DEV_VALUE_TOO_LOW = 0x09, + RETURN_DEV_VALUE_TOO_HIGH = 0x0A, + RETURN_DEV_I2C_BUS_FAILURE = 0x0B, + RETURN_SS_NOT_READY = 0x0C, + RETURN_SS_NOT_RESET_STATE = 0x0D +} ReturnStatus; + +#endif /* GLOBAL_HEADER_H_ */ diff --git a/firmware/psu/inc/common/i2cbus.h b/firmware/psu/inc/common/i2cbus.h new file mode 100644 index 0000000000..33c196e883 --- /dev/null +++ b/firmware/psu/inc/common/i2cbus.h @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#ifndef I2CBUS_H_ +#define I2CBUS_H_ + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include "inc/common/global_header.h" + +#include + +/***************************************************************************** + * STRUCT DEFINITIONS + *****************************************************************************/ +typedef struct I2C_Dev { + unsigned int bus; + uint8_t slave_addr; +} I2C_Dev; + +/***************************************************************************** + * FUNCTION DECLARATIONS + *****************************************************************************/ +I2C_Handle i2c_open_bus(unsigned int index); + +/* Wrapper to ease migration */ +#define i2c_get_handle i2c_open_bus + +void i2c_close_bus(I2C_Handle* i2cHandle); +ReturnStatus i2c_reg_write( I2C_Handle i2cHandle, + uint8_t deviceAddress, + uint8_t regAddress, + uint16_t value, + uint8_t numofBytes); +ReturnStatus i2c_reg_read( I2C_Handle i2cHandle, + uint8_t deviceAddress, + uint8_t regAddress, + uint16_t *value, + uint8_t numofBytes); + +#endif /* I2CBUS_H_ */ diff --git a/firmware/psu/inc/common/post.h b/firmware/psu/inc/common/post.h new file mode 100644 index 0000000000..26a8a7e046 --- /dev/null +++ b/firmware/psu/inc/common/post.h @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef POST_H_ +#define POST_H_ + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include "common/inc/global/ocmp_frame.h" +#include "common/inc/global/post_frame.h" +#include "inc/common/global_header.h" + +#include +#include + +/***************************************************************************** + * MACRO DEFINITIONS + *****************************************************************************/ +#define POST_RECORDS 55 + +#define OC_POST_TASKPRIORITY 3 +#define POST_TASK_STACK_SIZE 2048 + + +/***************************************************************************** + * HANDLE DECLARATIONS + *****************************************************************************/ +extern Semaphore_Handle semPOSTMsg; +extern Queue_Handle postRxMsgQueue; + +/***************************************************************************** + * FUNCTION DECLARATIONS + *****************************************************************************/ +void post_createtask(void); +#endif /* POST_H_ */ diff --git a/firmware/psu/inc/common/post_util.h b/firmware/psu/inc/common/post_util.h new file mode 100644 index 0000000000..69b6427577 --- /dev/null +++ b/firmware/psu/inc/common/post_util.h @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef INC_COMMON_POST_UTIL_H_ +#define INC_COMMON_POST_UTIL_H_ + +#include "common/inc/global/Framework.h" +#include "inc/common/post.h" + +ReturnStatus _execPost(OCMPMessageFrame *pMsg, + unsigned int subsystem_id); + +#endif /* INC_COMMON_POST_UTIL_H_ */ diff --git a/firmware/psu/inc/common/system_states.h b/firmware/psu/inc/common/system_states.h new file mode 100644 index 0000000000..9ee41569f1 --- /dev/null +++ b/firmware/psu/inc/common/system_states.h @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef SYSTEM_STATES_H_ +#define SYSTEM_STATES_H_ + +/***************************************************************************** + * ENUM DEFINITIONS + *****************************************************************************/ +typedef enum { + SS_STATE_PWRON = 1, + SS_STATE_INIT, + SS_STATE_CFG, + SS_STATE_RDY, + SS_STATE_ACTIVE, + SS_STATE_FAULTY, + SS_STATE_RESET, + SS_STATE_SHUTDOWN +} eSubSystemStates; + +typedef enum { + OC_STATE_PWRON = 1, + OC_STATE_POST, + OC_STATE_RDY, + OC_STATE_CONFIGURED, + OC_STATE_RF_ACTIVE, + OC_STATE_FAULTY, + OC_STATE_DEGRADED, + OC_STATE_LOST_SYNC, + OC_STATE_UPGRADING, + OC_STATE_RF_FAILURE, + OC_STATE_ETH_FAILURE +} eOCSystemState; + +#endif /* SYSTEM_STATES_H_ */ diff --git a/firmware/psu/inc/devices/debug_ocgpio.h b/firmware/psu/inc/devices/debug_ocgpio.h new file mode 100644 index 0000000000..cbc18a848c --- /dev/null +++ b/firmware/psu/inc/devices/debug_ocgpio.h @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef _OC_GPIO_H_ +#define _OC_GPIO_H_ + +#include "drivers/OcGpio.h" + +typedef struct __attribute__ ((packed, aligned(1))) { + uint8_t pin; + uint8_t value; +}S_OCGPIO; + +typedef struct S_OCGPIO_Cfg { + OcGpio_Port* port; + unsigned int group; +}S_OCGPIO_Cfg; + +#endif /* _OC_GPIO_H_ */ diff --git a/firmware/psu/inc/devices/debug_oci2c.h b/firmware/psu/inc/devices/debug_oci2c.h new file mode 100644 index 0000000000..af8b686efc --- /dev/null +++ b/firmware/psu/inc/devices/debug_oci2c.h @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef OC_I2C_H_ +#define OC_I2C_H_ + +#include + +typedef struct __attribute__ ((packed, aligned(1))){ + uint8_t slaveAddress; + uint8_t number_of_bytes; + uint8_t reg_address; + uint16_t reg_value; +}S_OCI2C; + +typedef struct S_I2C_Cfg { + unsigned int bus; +}S_I2C_Cfg; + +#endif /* INC_DEVICES_OC_I2C_H_ */ diff --git a/firmware/psu/inc/devices/eeprom.h b/firmware/psu/inc/devices/eeprom.h new file mode 100644 index 0000000000..b79b9a8fcc --- /dev/null +++ b/firmware/psu/inc/devices/eeprom.h @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef EEPROM_H_ +#define EEPROM_H_ + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include "common/inc/global/ocmp_frame.h" /* Temporary, just for OCMPSubsystem def */ +#include "drivers/OcGpio.h" +#include "inc/common/i2cbus.h" + +/***************************************************************************** + * MACRO DEFINITIONS + *****************************************************************************/ +#define OC_TEST_ADDRESS 0xFD0 +#define OC_CONNECT1_SERIAL_INFO 0x01C6 +#define OC_CONNECT1_SERIAL_SIZE 0x12 +#define OC_GBC_BOARD_INFO 0x01AC +#define OC_GBC_BOARD_INFO_SIZE 0x12 +#define OC_GBC_DEVICE_INFO 0x0100 /*TODO: Update offsets*/ +#define OC_SDR_BOARD_INFO 0x01AC +#define OC_SDR_BOARD_INFO_SIZE 0x12 +#define OC_SDR_DEVICE_INFO 0x0100 /*TODO: Update offsets*/ +#define OC_RFFE_BOARD_INFO 0x01AC +#define OC_RFFE_BOARD_INFO_SIZE 0x11 +#define OC_RFFE_DEVICE_INFO 0x0100 /*TODO: Update offsets*/ +#define OC_DEVICE_INFO_SIZE 0x0A + +/***************************************************************************** + * STRUCT DEFINITIONS + *****************************************************************************/ +typedef struct EepromDev_Cfg { + /*!< EEPROM size in bytes */ + size_t mem_size; + /*!< Page size (max bytes we can write in a single shot) */ + size_t page_size; +} EepromDev_Cfg; + +typedef struct Eeprom_Cfg { + I2C_Dev i2c_dev; + OcGpio_Pin *pin_wp; + EepromDev_Cfg type; /*!< Device specific config (page size, etc) */ + OCMPSubsystem ss; /* TODO: The HW config need not know about the subsytem + to be fixed later */ +} Eeprom_Cfg, *Eeprom_Handle; + +typedef enum { + OC_STAT_SYS_SERIAL_ID = 0, + OC_STAT_SYS_GBC_BOARD_ID, + OC_STAT_SYS_STATE +} eOCStatusParamId; + +/***************************************************************************** + * FUNCTION DECLARATIONS + *****************************************************************************/ +bool eeprom_init(Eeprom_Cfg *cfg); + +ReturnStatus eeprom_read(Eeprom_Cfg *cfg, + uint16_t address, + void *buffer, + size_t size); + +ReturnStatus eeprom_write(const Eeprom_Cfg *cfg, + uint16_t address, + const void *buffer, + size_t size); + +ReturnStatus eeprom_disable_write(Eeprom_Cfg *cfg); + +ReturnStatus eeprom_enable_write(Eeprom_Cfg *cfg); + +ReturnStatus eeprom_read_oc_info(uint8_t * oc_serial); + +ReturnStatus eeprom_read_board_info(const Eeprom_Cfg *cfg, + uint8_t * rom_info); + +ReturnStatus eeprom_read_device_info_record(const Eeprom_Cfg *cfg, + uint8_t recordNo, + char * device_info); + +ReturnStatus eeprom_write_device_info_record(Eeprom_Cfg *cfg, + uint8_t recordNo, + char * device_info); + +#endif /* EEPROM_H_ */ diff --git a/firmware/psu/inc/devices/ina226.h b/firmware/psu/inc/devices/ina226.h new file mode 100644 index 0000000000..13c03be6fe --- /dev/null +++ b/firmware/psu/inc/devices/ina226.h @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef INA226_H_ +#define INA226_H_ + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include "common/inc/global/post_frame.h" +#include "drivers/OcGpio.h" +#include "inc/common/i2cbus.h" + +/***************************************************************************** + * MACRO DEFINITIONS + *****************************************************************************/ +/* Mask/Enable Register Bits */ +#define INA_ALERT_EN_MASK 0xF800 /* Upper 5 bits are the enable bits */ +#define INA_MSK_SOL (1 << 15) /* Shunt over-voltage */ +#define INA_MSK_SUL (1 << 14) /* Shunt under-voltage */ +#define INA_MSK_BOL (1 << 13) /* Bus over-voltage */ +#define INA_MSK_BUL (1 << 12) /* Bus under-voltage */ +#define INA_MSK_POL (1 << 11) /* Power over limit */ +#define INA_MSK_CNVR (1 << 10) /* Conversion ready - enable alert when + * CVRF is set (ready for next conversion) */ + +#define INA_MSK_AFF (1 << 4) /* Alert Function Flag (caused by alert) + * In latch mode, cleared on mask read */ +#define INA_MSK_CVRF (1 << 3) /* Conversion Ready Flag, cleared when + * writing to cfg reg or mask read */ +#define INA_MSK_OVF (1 << 2) /* Math Overflow Flag (data may be invalid) */ +#define INA_MSK_APOL (1 << 1) /* Alert Polarity (1 = invert, active high) */ +#define INA_MSK_LEN (1 << 0) /* Alert Latch Enable + * 1 Latch (alert only cleared by read to msk) + * 0 Transparent (auto-clear on fault clear) */ + +#define INA_HYSTERESIS 30 /* 30mA TODO: need to make more robust, maybe percentage based */ + +/***************************************************************************** + * STRUCT/ENUM DEFINITIONS + *****************************************************************************/ +typedef enum INA226_Event { + INA226_EVT_SOL = INA_MSK_SOL, /* Shunt over-voltage */ + INA226_EVT_SUL = INA_MSK_SUL, /* Shunt under-voltage */ + INA226_EVT_BOL = INA_MSK_BOL, /* Bus over-voltage */ + INA226_EVT_BUL = INA_MSK_BUL, /* Bus under-voltage */ + INA226_EVT_POL = INA_MSK_POL, /* Power over limit */ + + /* Meta Events */ + INA226_EVT_COL, /* Current over limit - based on SOL */ + INA226_EVT_CUL, /* Current under limit - based on SUL */ +} INA226_Event; + +typedef void (*INA226_CallbackFn) (INA226_Event evt, uint16_t value, + void *context); + +typedef struct INA226_Cfg { + I2C_Dev dev; + OcGpio_Pin *pin_alert; +} INA226_Cfg; + +typedef struct INA226_Obj { + INA226_CallbackFn alert_cb; + void *cb_context; + INA226_Event evt_to_monitor; +} INA226_Obj; + +typedef struct INA226_Dev { + const INA226_Cfg cfg; + INA226_Obj obj; +} INA226_Dev; + +/***************************************************************************** + * FUNCTION DECLARATIONS + *****************************************************************************/ +ReturnStatus ina226_readCurrentLim(INA226_Dev *dev, uint16_t* currLimit); +ReturnStatus ina226_setCurrentLim(INA226_Dev *dev, uint16_t currLimit); +ReturnStatus ina226_readBusVoltage(INA226_Dev *dev, uint16_t* busVoltValue); +ReturnStatus ina226_readShuntVoltage(INA226_Dev *dev, + uint16_t* shuntVoltValue); +ReturnStatus ina226_readCurrent(INA226_Dev *dev, uint16_t* currValue); +ReturnStatus ina226_readPower(INA226_Dev *dev, uint16_t* powValue); +ReturnStatus ina226_init(INA226_Dev *dev); +void ina226_setAlertHandler(INA226_Dev *dev, INA226_CallbackFn alert_cb, + void *cb_context); +ReturnStatus ina226_enableAlert(INA226_Dev *dev, INA226_Event evt); +ePostCode ina226_probe(INA226_Dev *dev, POSTData *postData); +#endif /* INA226_H_ */ diff --git a/firmware/psu/inc/devices/int_battery.h b/firmware/psu/inc/devices/int_battery.h new file mode 100644 index 0000000000..16926ebc29 --- /dev/null +++ b/firmware/psu/inc/devices/int_battery.h @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef INT_BATTERY_H_ +#define INT_BATTERY_H_ + +/***************************************************************************** + * MACRO DEFINITIONS + *****************************************************************************/ +#define PWR_INT_BATT_RSNSB 30 //milli ohms +#define PWR_INT_BATT_RSNSI 7 //milli ohms + +/* Config parameters for Internal battery charger */ +#define PWR_INTBATT_UNDERVOLTAGE_VAL 9000 //milliVolts +#define PWR_INTBATT_OVERVOLTAGE_VAL 12600 //milliVolts +#define PWR_INTBATT_INPUTUNDERVOLATGE_VAL 16200 //milliVolts +#define PWR_INTBATT_INPUTOVERCURRENT_VAL 5000 //milliAmps +#define PWR_INTBATT_LOWBATTERYCURRENT_VAL 100 //milliAmps +#define PWR_INTBATT_INPUTCURRENTLIMIT_VAL 5570 //milliAmps + +#endif /* INT_BATTERY_H_ */ diff --git a/firmware/psu/inc/devices/ltc4015.h b/firmware/psu/inc/devices/ltc4015.h new file mode 100644 index 0000000000..47546f9c1e --- /dev/null +++ b/firmware/psu/inc/devices/ltc4015.h @@ -0,0 +1,199 @@ + +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef LTC4015_H_ +#define LTC4015_H_ + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include "common/inc/global/post_frame.h" +#include "drivers/OcGpio.h" +#include "inc/common/i2cbus.h" + +/***************************************************************************** + * MACRO DEFINITIONS + *****************************************************************************/ + +/* Mask/Enable Register Bits */ +#define LTC4015_ALERT_EN_MASK 0xFFFF /* Bits 15-0 are the enable bits(except bit 14) */ +#define LTC4015_MSK_MSRV (1 << 15) /* Measurement system results valid */ +#define LTC4015_MSK_QCL (1 << 13) /* QCOUNT Low alert */ +#define LTC4015_MSK_QCH (1 << 12) /* QCOUNT High alert */ +#define LTC4015_MSK_BVL (1 << 11) /* Battery voltage Low alert */ +#define LTC4015_MSK_BVH (1 << 10) /* Battery voltage High alert */ +#define LTC4015_MSK_IVL (1 << 9) /* Input voltage Low alert */ +#define LTC4015_MSK_IVH (1 << 8) /* Input voltage High alert */ +#define LTC4015_MSK_SVL (1 << 7) /* System voltage Low alert */ +#define LTC4015_MSK_SVH (1 << 6) /* System voltage High alert */ +#define LTC4015_MSK_ICH (1 << 5) /* Input current High alert */ +#define LTC4015_MSK_BCL (1 << 4) /* Battery current Low alert */ +#define LTC4015_MSK_DTH (1 << 3) /* Die temperature High alert */ +#define LTC4015_MSK_BSRH (1 << 2) /* BSR High alert */ +#define LTC4015_MSK_NTCH (1 << 1) /* NTC ratio High alert */ +#define LTC4015_MSK_NTCL (1 << 0) /* NTC ratio Low alert */ + +#define LTC4015_MSK_BMFA (1 << 1) /* Battery Missing Fault alert */ + +#define LTC4015_CHARGER_ENABLED (1 << 13) + +/***************************************************************************** + * STRUCT/ENUM DEFINITIONS + *****************************************************************************/ +/* Note: There are more chemistry settings that include fixed vs. programmable, + * however they don't matter as much as the overall chemistry grouping for + * selecting the correct conversion factors for the registers + */ +typedef enum LTC4015_Chem { + LTC4015_CHEM_LI_ION, + LTC4015_CHEM_LI_FE_PO4, + LTC4015_CHEM_LEAD_ACID, +} LTC4015_Chem; + +typedef enum LTC4015_Event { + LTC4015_EVT_MSRV = LTC4015_MSK_MSRV, /* Measurement system results valid */ + LTC4015_EVT_QCL = LTC4015_MSK_QCL, /* QCOUNT Low alert */ + LTC4015_EVT_QCH = LTC4015_MSK_QCH, /* QCOUNT High alert */ + LTC4015_EVT_BVL = LTC4015_MSK_BVL, /* Battery voltage Low alert */ + LTC4015_EVT_BVH = LTC4015_MSK_BVH, /* Battery voltage High alert */ + LTC4015_EVT_IVL = LTC4015_MSK_IVL, /* Input voltage Low alert */ + LTC4015_EVT_IVH = LTC4015_MSK_IVH, /* Input voltage High alert */ + LTC4015_EVT_SVL = LTC4015_MSK_SVL, /* System voltage Low alert */ + LTC4015_EVT_SVH = LTC4015_MSK_SVH, /* System voltage High alert */ + LTC4015_EVT_ICH = LTC4015_MSK_ICH, /* Input current High alert */ + LTC4015_EVT_BCL = LTC4015_MSK_BCL, /* Battery current Low alert */ + LTC4015_EVT_DTH = LTC4015_MSK_DTH, /* Die temperature High alert */ + LTC4015_EVT_BSRH = LTC4015_MSK_BSRH, /* BSR High alert */ + LTC4015_EVT_NTCL = LTC4015_MSK_NTCL, /* NTC ratio High alert */ + LTC4015_EVT_NTCH = LTC4015_MSK_NTCH, /* NTC ratio Low alert */ + + LTC4015_EVT_BMFA = LTC4015_MSK_BMFA, /* Battery Missing Fault alert */ +} LTC4015_Event; + +typedef void (*LTC4015_CallbackFn) (LTC4015_Event evt, int16_t value, + void *context); + +typedef struct LTC4015_HWCfg { + I2C_Dev i2c_dev; + + /* TODO: this can be read from the IC itself */ + LTC4015_Chem chem; /* Battery chemistry we're controlling (verified during init) */ + uint8_t r_snsb; /* Value of SNSB resistor in milli-ohms */ + uint8_t r_snsi; /* Value of SNSI resistor in milli-ohms */ + + /* TODO: this can be read from the IC itself */ + uint8_t cellcount; /* Number of cells in battery */ + + OcGpio_Pin *pin_alert; +} LTC4015_HWCfg; + +typedef struct LTC4015_Obj { + LTC4015_CallbackFn alert_cb; + void *cb_context; +} LTC4015_Obj; + +typedef struct LTC4015_Dev { + LTC4015_HWCfg cfg; + LTC4015_Obj obj; +} LTC4015_Dev; + +/***************************************************************************** + * FUNCTION DECLARATIONS + *****************************************************************************/ +ReturnStatus LTC4015_cfg_icharge(LTC4015_Dev *dev, + uint16_t max_chargeCurrent); + +ReturnStatus LTC4015_get_cfg_icharge(LTC4015_Dev *dev, + uint16_t *max_chargeCurrent); + +ReturnStatus LTC4015_cfg_vcharge(LTC4015_Dev *dev, + uint16_t charge_voltageLevel); + +ReturnStatus LTC4015_get_cfg_vcharge(LTC4015_Dev *dev, + uint16_t *charge_voltageLevel); + +ReturnStatus LTC4015_cfg_battery_voltage_low(LTC4015_Dev *dev, + int16_t underVoltage); + +ReturnStatus LTC4015_get_cfg_battery_voltage_low(LTC4015_Dev *dev, + int16_t *underVolatage); + +ReturnStatus LTC4015_cfg_battery_voltage_high(LTC4015_Dev *dev, + int16_t overVoltage); + +ReturnStatus LTC4015_get_cfg_battery_voltage_high(LTC4015_Dev *dev, + int16_t *overVoltage); + +ReturnStatus LTC4015_cfg_input_voltage_low(LTC4015_Dev *dev, + int16_t inputUnderVoltage); + +ReturnStatus LTC4015_get_cfg_input_voltage_low(LTC4015_Dev *dev, + int16_t *inpUnderVoltage); + +ReturnStatus LTC4015_cfg_input_current_high(LTC4015_Dev *dev, + int16_t inputOvercurrent); + +ReturnStatus LTC4015_get_cfg_input_current_high(LTC4015_Dev *dev, + int16_t *inpOverCurrent); + +ReturnStatus LTC4015_cfg_battery_current_low(LTC4015_Dev *dev, + int16_t lowbattCurrent); + +ReturnStatus LTC4015_get_cfg_battery_current_low(LTC4015_Dev *dev, + int16_t *lowbattCurrent); + +ReturnStatus LTC4015_cfg_die_temperature_high(LTC4015_Dev *dev, + int16_t dieTemp); + +ReturnStatus LTC4015_get_cfg_die_temperature_high(LTC4015_Dev *dev, + int16_t *dieTemp); + +ReturnStatus LTC4015_cfg_input_current_limit(LTC4015_Dev *dev, + uint16_t inputCurrentLimit); + +ReturnStatus LTC4015_get_cfg_input_current_limit(LTC4015_Dev *dev, + uint16_t *currentLimit); + +ReturnStatus LTC4015_get_die_temperature(LTC4015_Dev *dev, + int16_t *dieTemp); + +ReturnStatus LTC4015_get_battery_current(LTC4015_Dev *dev, + int16_t *iBatt); + +ReturnStatus LTC4015_get_input_current(LTC4015_Dev *dev, + int16_t *iIn); + +ReturnStatus LTC4015_get_battery_voltage(LTC4015_Dev *dev, + int16_t *vbat); + +ReturnStatus LTC4015_get_input_voltage(LTC4015_Dev *dev, + int16_t *vIn); + +ReturnStatus LTC4015_get_system_voltage(LTC4015_Dev *dev, + int16_t *vSys); + +ReturnStatus LTC4015_get_icharge_dac(LTC4015_Dev *dev, + int16_t *ichargeDac); + +ReturnStatus LTC4015_get_bat_presence(LTC4015_Dev *dev, + bool *present); + +ReturnStatus LTC4015_init(LTC4015_Dev *dev); + +void LTC4015_setAlertHandler(LTC4015_Dev *dev, LTC4015_CallbackFn alert_cb, + void *cb_context); + +ReturnStatus LTC4015_enableLimitAlerts(LTC4015_Dev *dev, uint16_t alert_mask); + +ReturnStatus LTC4015_enableChargerStateAlerts(LTC4015_Dev *dev, + uint16_t alert_mask); + +ePostCode LTC4015_probe(LTC4015_Dev *dev, POSTData *postData); + +#endif /* LTC4015_H_ */ diff --git a/firmware/psu/inc/devices/ltc4274.h b/firmware/psu/inc/devices/ltc4274.h new file mode 100644 index 0000000000..8362918e61 --- /dev/null +++ b/firmware/psu/inc/devices/ltc4274.h @@ -0,0 +1,169 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef LTC4274_H_ +#define LTC4274_H_ + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include "common/inc/global/post_frame.h" +#include "drivers/OcGpio.h" +#include "inc/common/global_header.h" +#include "inc/common/i2cbus.h" + +#include + +/* PSE Configuration */ +#define LTC4274_INTERRUPT_MASK 0x00 +#define LTC4274_OPERATING_MODE_SET 0x03 +#define LTC4274_DETCET_CLASS_ENABLE 0x11 +#define LTC4274_MISC_CONF 0xD1 + +/* PSE operating modes */ +#define LTC4274_SHUTDOWN_MODE 0x00 +#define LTC4274_MANUAL_MODE 0x01 +#define LTC4274_SEMIAUTO_MODE 0x02 +#define LTC4274_AUTO_MODE 0x03 + +#define LTC4274_INTERRUPT_ENABLE 0x80 +#define LTC4274_DETECT_ENABLE 0x40 +#define LTC4274_FAST_IV 0x20 +#define LTC4274_MSD_MASK 0x01 + +#define LTC4274_HP_ENABLE 0x11 + +/* POE Device Info */ +#define LTC4274_DEV_ID 0x0C +#define LTC4274_ADDRESS 0x2F +#define LTC4274_LTEPOE_90W 0x0E + +#define LTC4274_DEVID(x) (x>>3) +#define LTC4274_PWRGD(x) ((x&0x10)>>4) +#define LTC4374_CLASS(x) ((x&0xF0)>>4) /*if MSB is set it specifies LTEPOE++ device*/ +#define LTC4374_DETECT(x) ((x&0x07)) +#define LTC4274_DETECTION_COMPLETE(x) (x&0x01) +#define LTC4274_CLASSIFICATION_COMPLETE(x) (x&0x10) + +typedef enum LTC4274_Event { + LTC4274_EVT_SUPPLY = 1 << 8, + LTC4274_EVT_TSTART = 1 << 7, + LTC4274_EVT_TCUT = 1 << 6, + LTC4274_EVT_CLASS = 1 << 5, + LTC4274_EVT_DETECTION = 1 << 4, + LTC4274_EVT_DISCONNECT = 1 << 3, + LTC4274_EVT_POWERGOOD = 1 << 2, + LTC4274_EVT_POWER_ENABLE = 1 << 1, + LTC4274_EVT_NONE = 1 << 0, +} LTC4274_Event; + +typedef enum { + LTC4274_POWERGOOD = 0, + LTC4274_POWERGOOD_NOTOK +} ePSEPowerState; + +typedef enum { + LTC4274_DETECT_UNKOWN = 0, + LTC4274_SHORT_CIRCUIT, + LTC4274_CPD_HIGH, + LTC4274_RSIG_LOW, + LTC4274_SIGNATURE_GOOD, + LTC4274_RSIG_TOO_HIGH, + LTC4274_OPEN_CIRCUIT, + LTC4274_DETECT_ERROR +} ePSEDetection; + +typedef enum { + LTC4274_CLASSTYPE_UNKOWN = 0x01, + LTC4274_CLASSTYPE_1, + LTC4274_CLASSTYPE_2, + LTC4274_CLASSTYPE_3, + LTC4274_CLASSTYPE_4, + LTC4274_CLASSTYPE_RESERVED, + LTC4274_CLASSTYPE_0, + LTC4274_OVERCURRENT, + LTC4274_LTEPOE_TYPE_52_7W =0x09, + LTC4274_LTEPOE_TYPE_70W =0x0a, + LTC4274_LTEPOE_TYPE_90W=0x0b, + LTC4274_LTEPOE_TYPE_38_7W=0xe, + LTC4274_LTEPOE_RESERVED, + LTC4274_CLASS_ERROR +} ePSEClassType; + +typedef enum { + LTC4274_STATE_OK = 0, + LTC4274_STATE_NOTOK +} ePSEState; + +typedef enum { + LTC4274_NO_ACTIVE_ALERT = 0x00, + LTC4274_POWER_ENABLE_ALERT = 0x01, + LTC4274_POWERGOOD_ALERT = 0x02, + LTC4274_DISCONNECT_ALERT = 0x04, + LTC4274_DETECTION_ALERT = 0x08, + LTC4274_CLASS_ALERT = 0x10, + LTC4274_TCUT_ALERT = 0x20, + LTC4274_TSTART_ALERT = 0x40, + LTC4274_SUPPLY_ALERT = 0x80 +} ePSEAlert; + +typedef void (*LTC4274_CallbackFn) (LTC4274_Event evt, + void *context); + +typedef struct LTC4274_Cfg { + I2C_Dev i2c_dev; + OcGpio_Pin *pin_evt; + OcGpio_Pin reset_pin; +} LTC4274_Cfg; + +typedef struct LTC4274_Obj { + LTC4274_CallbackFn alert_cb; + void *cb_context; + GateMutex_Handle mutex; +} LTC4274_Obj; + +typedef struct LTC4274_Dev { + const LTC4274_Cfg cfg; + LTC4274_Obj obj; +} LTC4274_Dev; + +ReturnStatus ltc4274_set_cfg_operation_mode(const I2C_Dev *i2c_dev, uint8_t operatingMode); +ReturnStatus ltc4274_get_operation_mode(const I2C_Dev *i2c_dev, uint8_t *operatingMode); +ReturnStatus ltc4274_set_cfg_detect_enable(const I2C_Dev *i2c_dev, uint8_t detectEnable); +ReturnStatus ltc4274_get_detect_enable(const I2C_Dev *i2c_dev, uint8_t *detectVal); +ReturnStatus ltc4274_set_interrupt_mask(const I2C_Dev *i2c_dev, uint8_t interruptMask); +ReturnStatus ltc4274_get_interrupt_mask(const I2C_Dev *i2c_dev, uint8_t *intrMask); +ReturnStatus ltc4274_cfg_interrupt_enable(const I2C_Dev *i2c_dev, bool enable); +ReturnStatus ltc4274_get_interrupt_enable(const I2C_Dev *i2c_dev, uint8_t *interruptEnable); +ReturnStatus ltc4274_set_cfg_pshp_feature(const I2C_Dev *i2c_dev, uint8_t hpEnable); +ReturnStatus ltc4274_get_pshp_feature(const I2C_Dev *i2c_dev, uint8_t *hpEnable); +ReturnStatus ltc4274_get_detection_status(const I2C_Dev *i2c_dev, ePSEDetection *pseDetect); +ReturnStatus ltc4274_get_class_status(const I2C_Dev *i2c_dev, ePSEClassType *pseClass); +ReturnStatus ltc4274_get_powergood_status(const I2C_Dev *i2c_dev, uint8_t *psePwrGood); +void ltc4274_set_alert_handler(LTC4274_Dev *dev, LTC4274_CallbackFn alert_cb, void *cb_context); +ReturnStatus ltc4274_clear_interrupt( const I2C_Dev *i2c_dev, + uint8_t *pwrEvent, + uint8_t *overCurrent, + uint8_t *supply); +ReturnStatus ltc4274_get_interrupt_status(const I2C_Dev *i2c_dev, uint8_t *val); +ReturnStatus ltc4274_debug_write(const I2C_Dev *i2c_dev, + uint8_t reg_address, uint8_t value); +ReturnStatus ltc4274_debug_read(const I2C_Dev *i2c_dev, + uint8_t reg_address, uint8_t *value); +void ltc4274_enable(LTC4274_Dev *dev, uint8_t enableVal); +ReturnStatus ltc4274_get_devid(const I2C_Dev *i2c_dev, + uint8_t *devID); +ReturnStatus ltc4274_detect(const I2C_Dev *i2c_dev, + uint8_t *detect, uint8_t *val); +ePostCode ltc4274_probe(const LTC4274_Dev *i2c_dev, POSTData *postData); +void ltc4274_init(LTC4274_Dev *dev); +void ltc4274_initPSEStateInfo(); +void ltc4274_update_stateInfo(const I2C_Dev *i2c_dev); +ReturnStatus ltc4274_reset(); + +#endif /* LTC4274_H_ */ diff --git a/firmware/psu/inc/devices/ltc4295.h b/firmware/psu/inc/devices/ltc4295.h new file mode 100644 index 0000000000..4d82f62906 --- /dev/null +++ b/firmware/psu/inc/devices/ltc4295.h @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef LTC4295_H_ +#define LTC4295_H_ + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include "common/inc/global/post_frame.h" +#include "drivers/OcGpio.h" +#include "inc/common/global_header.h" + +#include + +typedef enum { + LTC4295_STATUS_CLASS = 0x00, + LTC4295_STATUS_POWERGOOD = 0x01, +}eltc4295StatusParamId; + +typedef enum { + LTC4295_POWERGOOD = 0, + LTC4295_POWERGOOD_NOTOK +} ePDPowerState; + +typedef enum { + LTC4295_CLASSTYPE_UNKOWN = 0, + LTC4295_CLASSTYPE_1, + LTC4295_CLASSTYPE_2, + LTC4295_CLASSTYPE_3, + LTC4295_CLASSTYPE_POEPP +} ePDClassType; + +typedef enum { + LTC4295_STATE_OK = 0, + LTC4295_STATE_NOTOK +} ePDState; + +typedef enum { + LTC4295_CONNECT_ALERT = 1, + LTC4295_DISCONNECT_ALERT, + LTC4295_INCOMPATIBLE_ALERT +} ePDAlert; + +typedef enum { + LTC4295_CONNECT_EVT = 1 << 2, /* PD device Connected. */ + LTC4295_DISCONNECT_EVT = 1 << 1, /* PD device removed. */ + LTC4295_INCOMPATIBLE_EVT = 1 << 0, /* Incomaptible device */ +} LTC4295_Event; + +typedef struct __attribute__((packed, aligned(1))) { + uint8_t classStatus; + uint8_t powerGoodStatus; +}tPower_PDStatus; + +typedef struct __attribute__((packed, aligned(1))) { + tPower_PDStatus pdStatus; + ePDState state; + ePDAlert pdalert; +}tPower_PDStatus_Info; + +typedef struct LTC4295_Cfg { + OcGpio_Pin *pin_evt; + OcGpio_Pin *pin_detect; +} LTC4295_Cfg; + +typedef void (*LTC4295_CallbackFn) (LTC4295_Event evt, + void *context); +typedef struct LTC4295_Obj { + LTC4295_CallbackFn alert_cb; + void *cb_context; + GateMutex_Handle mutex; +} LTC4295_Obj; + +typedef struct LTC4295A_Dev { + const LTC4295_Cfg cfg; + LTC4295_Obj obj; +} LTC4295_Dev; + +/***************************************************************************** + * FUNCTION DECLARATIONS + *****************************************************************************/ +void ltc4295_config(const LTC4295_Dev *dev); +ePostCode ltc4295_probe(const LTC4295_Dev *dev, POSTData *postData); +ReturnStatus ltc4295_init(LTC4295_Dev *dev); +void ltc4295_set_alert_handler(LTC4295_Dev *dev, LTC4295_CallbackFn alert_cb, void *cb_context); +ReturnStatus ltc4295_get_power_good(const LTC4295_Dev *dev, ePDPowerState *val); +ReturnStatus ltc4295_get_class(const LTC4295_Dev *dev, ePDClassType *val); +void ltc4295_update_status(const LTC4295_Dev *dev); + +#endif /* LTC4295_H_ */ diff --git a/firmware/psu/inc/devices/powerSource.h b/firmware/psu/inc/devices/powerSource.h new file mode 100644 index 0000000000..0dabac2f3c --- /dev/null +++ b/firmware/psu/inc/devices/powerSource.h @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef POWERSOURCE_H_ +#define POWERSOURCE_H_ + +#include "common/inc/global/post_frame.h" +#include "common/inc/global/ocmp_frame.h" +#include "common/inc/global/Framework.h" +#include "drivers/OcGpio.h" +#include "inc/common/global_header.h" +#include "inc/common/i2cbus.h" + +#include + +typedef enum { + PWR_SRC_EXT = 0, + PWR_SRC_POE, + PWR_SRC_LIION_BATT, + PWR_SRC_MAX +} ePowerSource; + +typedef enum { + PWR_SRC_ACTIVE = 0, /* If source is primary source */ + PWR_SRC_AVAILABLE, /* If source is available */ + PWR_SRC_NON_AVAILABLE /* If source is not connected */ +} ePowerSourceState; + +typedef struct { + ePowerSource powerSource; + ePowerSourceState state; +} tPowerSource; + +typedef struct PWRSRC_Cfg { + OcGpio_Pin pin_dc_present; + OcGpio_Pin pin_poe_prsnt_n; + OcGpio_Pin pin_int_bat_prsnt; + OcGpio_Pin pin_disable_dc_input; + OcGpio_Pin pin_dc_input_fault; + OcGpio_Pin pin_oc_input_present; + OcGpio_Pin pin_power_off; +} PWRSRC_Cfg; + +typedef struct PWRSRC_Cfg_Obj { + GateMutex_Handle mutex; +} PWRSRC_Obj; + +typedef struct PWRSRC_Dev { + const PWRSRC_Cfg cfg; + PWRSRC_Obj obj; +} PWRSRC_Dev; + +typedef enum { + PWR_STAT_EXT_PWR_AVAILABILITY, + PWR_STAT_EXT_PWR_ACTIVE, + PWR_STAT_POE_AVAILABILITY, + PWR_STAT_POE_ACTIVE, + PWR_STAT_BATT_AVAILABILITY, + PWR_STAT_BATT_ACTIVE +} ePower_StatusParamId; + +void pwr_source_init(void); +void pwr_get_source_info(PWRSRC_Dev *pwrSrcDev); + +#endif /* POWERSOURCE_H_ */ diff --git a/firmware/psu/inc/devices/se98a.h b/firmware/psu/inc/devices/se98a.h new file mode 100644 index 0000000000..3b3be07741 --- /dev/null +++ b/firmware/psu/inc/devices/se98a.h @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef SE98A_H_ +#define SE98A_H_ + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include "common/inc/global/post_frame.h" +#include "common/inc/global/Framework.h" +#include "drivers/OcGpio.h" +#include "inc/common/i2cbus.h" + +#include + +/***************************************************************************** + * STRUCT/ENUM DEFINITIONS + *****************************************************************************/ +typedef enum SE98A_Event { + SE98A_EVT_ACT = 1 << 2, /* Above critical temp */ + SE98A_EVT_AAW = 1 << 1, /* Above alarm window */ + SE98A_EVT_BAW = 1 << 0, /* Below alarm window */ +} SE98A_Event; + +typedef enum +{ + CONF_TEMP_SE98A_LOW_LIMIT_REG = 1, + CONF_TEMP_SE98A_HIGH_LIMIT_REG, + CONF_TEMP_SE98A_CRITICAL_LIMIT_REG +}eTempSensor_ConfigParamsId; + +typedef void (*SE98A_CallbackFn) (SE98A_Event evt, int8_t temperature, + void *context); + +typedef struct SE98A_Cfg { + I2C_Dev dev; + OcGpio_Pin *pin_evt; +} SE98A_Cfg; + +typedef struct SE98A_Obj { + SE98A_CallbackFn alert_cb; + void *cb_context; + GateMutex_Handle mutex; +} SE98A_Obj; + +typedef struct SE98A_Dev { + const SE98A_Cfg cfg; + SE98A_Obj obj; +} SE98A_Dev; + +/***************************************************************************** + * FUNCTION DECLARATIONS + *****************************************************************************/ +/*! Initializes an SE98A device + * @param dev Pointer to the device struct containing hw config and object data + * @return RETURN_OK on success, error code on failure + */ +ReturnStatus se98a_init(SE98A_Dev *dev); + +/*! Registers a callback function to process alerts from the sensor + * @param dev Device struct pointer + * @param alert_db Function to call when alert is triggered + * @param cb_context Pointer to pass to callback (optional) + */ +void se98a_set_alert_handler(SE98A_Dev *dev, SE98A_CallbackFn alert_cb, + void *cb_context); + +/*! Enables the alert output pin on the SE98A + * @param dev Device struct pointer + * @return RETURN_OK on success, error code on failure + */ +ReturnStatus se98a_enable_alerts(SE98A_Dev *dev); + +/* TODO: this is for legacy support for how subsystems currently perform + * POST (probe device, then init) - I propose that this should all be + * in a single function */ +/*! Tests the SE98A device and verifies that the driver supports it + * @param dev Device struct pointer, Post data struct + * @return POST_DEV_FOUND on success, error code on failure + */ +ePostCode se98a_probe(SE98A_Dev *dev, POSTData *postData); + +/*! Sets one of the 3 alert thresholds on the device + * @param dev Device struct pointer + * @param limitToConfig Which of the 3 limits to configure + * @param tempLimitValue The temperature to set the threshold to + * @return RETURN_OK on success, error code on failure + */ +ReturnStatus se98a_set_limit(SE98A_Dev *dev, + eTempSensor_ConfigParamsId limitToConfig, + int8_t tempLimitValue); + +/*! Reads one of the 3 alert thresholds on the device + * @param dev Device struct pointer + * @param limitToConfig Which of the 3 limits to read + * @param tempLimitValue Outparam containing the returned threshold + * @return RETURN_OK on success, error code on failure + */ +ReturnStatus se98a_get_limit(SE98A_Dev *dev, + eTempSensor_ConfigParamsId limitToConfig, + int8_t* tempLimitValue); + +/*! Reads the current temperature from the sensor + * @param dev Device struct pointer + * @param tempValue Outval containing the measured temperature + * @return RETURN_OK on success, error code on failure + */ +ReturnStatus se98a_read(SE98A_Dev *dev, int8_t *tempValue); + +#endif /* SE98A_H_ */ diff --git a/firmware/psu/inc/devices/sx1509.h b/firmware/psu/inc/devices/sx1509.h new file mode 100644 index 0000000000..09741bd63c --- /dev/null +++ b/firmware/psu/inc/devices/sx1509.h @@ -0,0 +1,141 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef SX1509_H_ +#define SX1509_H_ + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include "inc/common/global_header.h" +#include "inc/common/i2cbus.h" + +/***************************************************************************** + * MACRO DEFINITIONS + *****************************************************************************/ +/* Oscillator frequency source */ +#define SX1509_EXTERNAL_CLOCK 1 +#define SX1509_INTERNAL_CLOCK_2MHZ 2 + +/* OSCIO pin function */ +#define SX1509_CLOCK_OSC_IN 0 +#define SX1509_CLOCK_OSC_OUT 1 + +/* IO pin definitions */ +#define SX1509_IO_PIN_0 0x0001 +#define SX1509_IO_PIN_1 0x0002 +#define SX1509_IO_PIN_2 0x0004 +#define SX1509_IO_PIN_3 0x0008 +#define SX1509_IO_PIN_4 0x0010 +#define SX1509_IO_PIN_5 0x0020 +#define SX1509_IO_PIN_6 0x0040 +#define SX1509_IO_PIN_7 0x0080 +#define SX1509_IO_PIN_8 0x0001 +#define SX1509_IO_PIN_9 0x0002 +#define SX1509_IO_PIN_10 0x0004 +#define SX1509_IO_PIN_11 0x0008 +#define SX1509_IO_PIN_12 0x0010 +#define SX1509_IO_PIN_13 0x0020 +#define SX1509_IO_PIN_14 0x0040 +#define SX1509_IO_PIN_15 0x0080 + +/***************************************************************************** + * STRUCT/ENUM DEFINITIONS + *****************************************************************************/ +/* Enumeration of SX1509 register types */ +typedef enum { + SX1509_REG_A = 0, + SX1509_REG_B, + SX1509_REG_AB +} sx1509RegType; + +typedef enum { + SX1509_EDGE_SENSE_REG_LOW = 0, + SX1509_EDGE_SENSE_REG_HIGH, + SX1509_EDGE_SENSE_REG_LOW_HIGH +} sx1509EdgeSenseRegType; + +/***************************************************************************** + * FUNCTION DECLARATIONS + *****************************************************************************/ +ReturnStatus ioexp_led_get_data(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t *regValue); +ReturnStatus ioexp_led_set_data(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t regValue1, + uint8_t regValue2); +ReturnStatus ioexp_led_set_on_time(const I2C_Dev *i2c_dev, + uint8_t index, + uint8_t tOnRegValue); +ReturnStatus ioexp_led_set_off_time(const I2C_Dev *i2c_dev, + uint8_t index, + uint8_t tOffRegValue); +ReturnStatus ioexp_led_software_reset(const I2C_Dev *i2c_dev); +ReturnStatus ioexp_led_config_inputbuffer(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t inputBuffRegValue1, + uint8_t inputBuffRegValue2); +ReturnStatus ioexp_led_config_pullup(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t pullUpRegValue1, + uint8_t pullUpRegValue2); +ReturnStatus ioexp_led_config_pulldown(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t pullDownRegValue1, + uint8_t pullDownRegValue2); +ReturnStatus ioexp_led_config_opendrain(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t openDrainRegValue1, + uint8_t openDrainRegValue2); +ReturnStatus ioexp_led_config_data_direction(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t directionRegValue1, + uint8_t directionRegValue2); +ReturnStatus ioexp_led_config_polarity(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t polarityRegValue1, + uint8_t polarityRegValue2); +ReturnStatus ioexp_led_config_clock(const I2C_Dev *i2c_dev, + uint8_t oscSource, + uint8_t oscPin); +ReturnStatus ioexp_led_config_misc(const I2C_Dev *i2c_dev, + uint8_t regValue); +ReturnStatus ioexp_led_enable_leddriver(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t ledEnableRegValue1, + uint8_t ledEnableRegValue2); +ReturnStatus ioexp_led_read_testregister_1(const I2C_Dev *i2c_dev, + uint8_t *regValue); +ReturnStatus ioexp_led_config_interrupt(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t interruptMaskRegValue1, + uint8_t interruptMaskRegValue2); +ReturnStatus ioexp_led_config_edge_sense_A(const I2C_Dev *i2c_dev, + sx1509EdgeSenseRegType regType, + uint8_t edgeSenseLowARegValue, + uint8_t edgeSenseHighARegValue); +ReturnStatus ioexp_led_config_edge_sense_A(const I2C_Dev *i2c_dev, + sx1509EdgeSenseRegType regType, + uint8_t edgeSenseLowBRegValue, + uint8_t edgeSenseHighBRegValue); +ReturnStatus ioexp_led_config_edge_sense_B(const I2C_Dev *i2c_dev, + sx1509EdgeSenseRegType regType, + uint8_t edgeSenseLowBRegValue, + uint8_t edgeSenseHighBRegValue); +ReturnStatus ioexp_led_config_debounce_time(const I2C_Dev *i2c_dev, + uint8_t debounceTime); +ReturnStatus ioexp_led_enable_debounce(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t debounceEnableRegValue1, + uint8_t debounceEnableRegValue2); +ReturnStatus ioexp_led_get_interrupt_source(const I2C_Dev *i2c_dev, + uint16_t *intPins); +ReturnStatus ioexp_led_clear_interrupt_source(const I2C_Dev *i2c_dev); + +#endif /* SX1509_H_ */ diff --git a/firmware/psu/inc/interfaces/uartdma.h b/firmware/psu/inc/interfaces/uartdma.h new file mode 100644 index 0000000000..b2c77d1f6a --- /dev/null +++ b/firmware/psu/inc/interfaces/uartdma.h @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef UARTDMA_H_ +#define UARTDMA_H_ + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include "inc/utils/util.h" + +/***************************************************************************** + * MACROS DEFINITION + *****************************************************************************/ +#define OCUARTDMA_TASK_PRIORITY 5 +//#define OCUARTDMA_TASK_PRIORITY 7 +#define OCUARTDMA_TASK_STACK_SIZE 1024 + +#define OCUARTDMATX_TASK_PRIORITY 7 +#define OCUARTDMATX_TASK_STACK_SIZE 1024 + +#define UART_TXBUF_SIZE OCMP_FRAME_TOTAL_LENGTH +#define UART_RXBUF_SIZE OCMP_FRAME_TOTAL_LENGTH + +/***************************************************************************** + * HANDLE DECLARATIONS + *****************************************************************************/ +extern Semaphore_Handle semUARTTX; +extern Queue_Handle uartTxMsgQueue; + +/***************************************************************************** + * FUNCTION DECLARATIONS + *****************************************************************************/ +void uDMAIntHandler(void); +void uDMAErrorHandler(void); +void UART4IntHandler(void); +void resetUARTDMA(void); +void ConfigureUART(void); +void InitUART4Transfer(void); +void dataTransfertoProc(char *buffer, int size); +void uartdma_init(void); +void uartDMAinterface_init(void); + +void uartdma_tx_taskinit(void); +void uartdma_rx_createtask(void); +void uartdma_tx_createtask(void); + +#endif /* UARTDMA_H_ */ diff --git a/firmware/psu/inc/subsystem/hci/hci.h b/firmware/psu/inc/subsystem/hci/hci.h new file mode 100644 index 0000000000..ad17ff6e07 --- /dev/null +++ b/firmware/psu/inc/subsystem/hci/hci.h @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef HCI_H_ +#define HCI_H_ + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include "drivers/OcGpio.h" +#include "inc/subsystem/hci/hci_buzzer.h" + +/***************************************************************************** + * MACRO DEFINITIONS + *****************************************************************************/ +#define HCI_TASK_PRIORITY 6 +#define HCI_TASK_STACK_SIZE 1024 + +#define HCI_LED_TEMP_SENSOR_ADDR 0x1A + +/***************************************************************************** + * STRUCT/ENUM DEFINITIONS + *****************************************************************************/ +/* Subsystem config */ +typedef struct Hci_Cfg { + HciBuzzer_Cfg buzzer; +} Hci_Cfg; + +/***************************************************************************** + * FUNCTION DECLARATIONS + *****************************************************************************/ +bool HCI_Init(void *driver, void *return_buf); + +#endif /* HCI_H_ */ diff --git a/firmware/psu/inc/subsystem/hci/hci_buzzer.h b/firmware/psu/inc/subsystem/hci/hci_buzzer.h new file mode 100644 index 0000000000..04476b962b --- /dev/null +++ b/firmware/psu/inc/subsystem/hci/hci_buzzer.h @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef _HCI_BUZZER_H +#define _HCI_BUZZER_H + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include "drivers/OcGpio.h" +#include "inc/common/global_header.h" + +#include + +/***************************************************************************** + * STRUCT/ENUM DEFINITIONS + *****************************************************************************/ +/* Subsystem config */ +typedef struct HciBuzzer_Cfg { + OcGpio_Pin pin_en; +} HciBuzzer_Cfg; + +/***************************************************************************** + * FUNCTION DECLARATIONS + *****************************************************************************/ +ReturnStatus HciBuzzer_init(void); +ReturnStatus hci_buzzer_beep(uint8_t buzzCount); + +#endif /* _HCI_BUZZER_H */ diff --git a/firmware/psu/inc/subsystem/power/power.h b/firmware/psu/inc/subsystem/power/power.h new file mode 100644 index 0000000000..cd8121f272 --- /dev/null +++ b/firmware/psu/inc/subsystem/power/power.h @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef POWER_H_ +#define POWER_H_ + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include "inc/devices/ltc4015.h" +#include "inc/devices/ltc4274.h" +#include "inc/devices/ltc4295.h" +#include "inc/devices/powerSource.h" +#include "inc/devices/se98a.h" + +bool power_Init(void *driver, void *returnValue); +bool psuCore_pre_init(void *driver, void *returnValue); + +#endif diff --git a/firmware/psu/inc/subsystem/watchdog/watchdog.h b/firmware/psu/inc/subsystem/watchdog/watchdog.h new file mode 100644 index 0000000000..a8b704bd46 --- /dev/null +++ b/firmware/psu/inc/subsystem/watchdog/watchdog.h @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef WATCHDOG_H_ +#define WATCHDOG_H_ + +/***************************************************************************** + * MACRO DEFINITIONS + *****************************************************************************/ +#define WATCHDOG_TASK_STACK_SIZE 1024 +#define WATCHDOG_TASK_PRIORITY 2 + +#endif /* WATCHDOG_H_ */ diff --git a/firmware/psu/inc/utils/ocmp_util.h b/firmware/psu/inc/utils/ocmp_util.h new file mode 100644 index 0000000000..9aae16ebc4 --- /dev/null +++ b/firmware/psu/inc/utils/ocmp_util.h @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef INC_UTILS_OCMP_UTIL_H_ +#define INC_UTILS_OCMP_UTIL_H_ + +#include "common/inc/global/ocmp_frame.h" + +#include +#include +#include + +/***************************************************************************** + ** FUNCTION NAME : OCMP_mallocFrame + ** + ** DESCRIPTION : Allocates memory for OCMP packets. + ** + ** ARGUMENTS : length + ** + ** RETURN TYPE : OCMPMessageFrame + ** + *****************************************************************************/ + +OCMPMessageFrame * OCMP_mallocFrame(uint16_t len); + +/***************************************************************************** + ** FUNCTION NAME : create_ocmp_msg_frame + ** + ** DESCRIPTION : Create a OCMP message. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : OCMPMessageFrame + ** + *****************************************************************************/ +OCMPMessageFrame* create_ocmp_msg_frame(OCMPSubsystem subSystem, + OCMPMsgType msgtype, + OCMPActionType actionType, + uint8_t componentId, + uint16_t parameters, + uint8_t payloadSize); + + +/***************************************************************************** + ** FUNCTION NAME : create_ocmp_alert_from_Evt + ** + ** DESCRIPTION : Create the OCMP Alert frame from the Event message. + ** + ** ARGUMENTS : OCMPMessageFrame to be used to create Alert, + ** ComponentId, + ** ParemeterID + ** + ** RETURN TYPE : OCMPMessageFrame + ** + *****************************************************************************/ +OCMPMessageFrame* create_ocmp_alert_from_Evt(OCMPMessageFrame* ocmpEventMsg, + uint8_t componentId, + uint16_t parameters ); + +#endif /* INC_UTILS_OCMP_UTIL_H_ */ diff --git a/firmware/psu/inc/utils/util.h b/firmware/psu/inc/utils/util.h new file mode 100644 index 0000000000..26d3ebc87b --- /dev/null +++ b/firmware/psu/inc/utils/util.h @@ -0,0 +1,207 @@ +/******************************************************************************* + Filename: util.h + Revised: $Date: 2015-07-07 13:30:52 -0700 (Tue, 07 Jul 2015) $ + Revision: $Revision: 44319 $ + + Description: This file contains function declarations common to CC26xx + TIRTOS Applications. + + Copyright 2014 Texas Instruments Incorporated. All rights reserved. + + IMPORTANT: Your use of this Software is limited to those specific rights + granted under the terms of a software license agreement between the user + who downloaded the software, his/her employer (which must be your employer) + and Texas Instruments Incorporated (the "License"). You may not use this + Software unless you agree to abide by the terms of the License. The License + limits your use, and you acknowledge, that the Software may not be modified, + copied or distributed unless embedded on a Texas Instruments microcontroller + or used solely and exclusively in conjunction with a Texas Instruments radio + frequency transceiver, which is integrated into your product. Other than for + the foregoing purpose, you may not use, reproduce, copy, prepare derivative + works of, modify, distribute, perform, display or sell this Software and/or + its documentation for any purpose. + + YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE + PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, + INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE, + NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL + TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT, + NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER + LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES + INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE + OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT + OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES + (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS. + + Should you have any questions regarding your right to use this Software, + contact Texas Instruments Incorporated at www.TI.com. +*******************************************************************************/ + +#ifndef UTIL_H +#define UTIL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************************************************************* + * INCLUDES + */ +#include +#include +#include +#include +#include +#include + +/********************************************************************* +* EXTERNAL VARIABLES +*/ + +/********************************************************************* + * CONSTANTS + */ + +/********************************************************************* + * TYPEDEFS + */ + +typedef struct +{ + uint16_t event; // Event type. + uint8_t state; // Event state; +}appEvtHdr_t; + +/********************************************************************* + * MACROS + */ + +/********************************************************************* + * API FUNCTIONS + */ + +/********************************************************************* + * @fn Util_constructClock + * + * @brief Initialize a TIRTOS Clock instance. + * + * @param pClock - pointer to clock instance structure. + * @param clockCB - callback function upon clock expiration. + * @param clockDuration - longevity of clock timer in milliseconds + * @param clockPeriod - duration of a periodic clock, used continuously + * after clockDuration expires. + * @param startFlag - TRUE to start immediately, FALSE to wait. + * @param arg - argument passed to callback function. + * + * @return Clock_Handle - a handle to the clock instance. + */ +Clock_Handle Util_constructClock(Clock_Struct *pClock, + Clock_FuncPtr clockCB, + uint32_t clockDuration, + uint32_t clockPeriod, + uint8_t startFlag, + UArg arg); + +/********************************************************************* + * @fn Util_startClock + * + * @brief Start a clock. + * + * @param pClock - pointer to clock struct + * + * @return none + */ +void Util_startClock(Clock_Struct *pClock); + +/********************************************************************* + * @fn Util_setClockTimeout + * + * @brief Restart a clock by changing the timeout. + * + * @param pClock - pointer to clock struct + * @param clockTimeout - longevity of clock timer in milliseconds + * + * @return none + */ +void Util_restartClock(Clock_Struct *pClock, uint32_t clockTimeout); + +/********************************************************************* + * @fn Util_isActive + * + * @brief Determine if a clock is currently active. + * + * @param pClock - pointer to clock struct + * + * @return TRUE or FALSE + */ +bool Util_isActive(Clock_Struct *pClock); + +/********************************************************************* + * @fn Util_stopClock + * + * @brief Stop a clock. + * + * @param pClock - pointer to clock struct + * + * @return none + */ +void Util_stopClock(Clock_Struct *pClock); + +/********************************************************************* + * @fn Util_rescheduleClock + * + * @brief Reschedule a clock by changing the timeout and period values. + * + * @param pClock - pointer to clock struct + * @param clockPeriod - longevity of clock timer in milliseconds + * @return none + */ +void Util_rescheduleClock(Clock_Struct *pClock, uint32_t clockPeriod); + +/********************************************************************* + * @fn Util_constructQueue + * + * @brief Initialize an RTOS queue to hold messages from profile to be + * processed. + * + * @param pQueue - pointer to queue instance structure. + * + * @return A queue handle. + */ +Queue_Handle Util_constructQueue(Queue_Struct *pQueue); + +/********************************************************************* + * @fn Util_enqueueMsg + * + * @brief Creates a queue node and puts the node in RTOS queue. + * + * @param msgQueue - queue handle. + * + * @param sem - the thread's event processing semaphore that this queue is + * associated with. + * + * @param pMsg - pointer to message to be queued + * + * @return TRUE if message was queued, FALSE otherwise. + */ +uint8_t Util_enqueueMsg(Queue_Handle msgQueue, Semaphore_Handle sem, uint8_t *pMsg); + +/********************************************************************* + * @fn Util_dequeueMsg + * + * @brief Dequeues the message from the RTOS queue. + * + * @param msgQueue - queue handle. + * + * @return pointer to dequeued message, NULL otherwise. + */ +uint8_t *Util_dequeueMsg(Queue_Handle msgQueue); + +/********************************************************************* +*********************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* UTIL_H */ diff --git a/firmware/psu/makefile.defs b/firmware/psu/makefile.defs new file mode 100644 index 0000000000..5a0353ce0c --- /dev/null +++ b/firmware/psu/makefile.defs @@ -0,0 +1,24 @@ +#File used to help "Clean Project" in CCS completely clean the kernel files +CFG_SRCDIR = ../src + +ifneq (,$(findstring :,$(WINDIR)$(windir)$(COMSPEC)$(comspec))) + # if Windows, use copy to touch file dates + TOUCH = copy /b $(subst /,\,$@)+,, $(subst /,\,$@) +else + TOUCH = touch $@ +endif + +# include Config generated top-level makefile +-include $(CFG_SRCDIR)/makefile.libs + +ifneq (clean,$(MAKECMDGOALS)) +# ensure this file is reloaded when .cfg files change but after config runs +$(CFG_SRCDIR)/makefile.libs: $(GEN_OPTS) $(CFG_SRCS) + -@$(if $(wildcard $@),$(TOUCH),:) +endif + +#add generated makefile to list of files to delete during a clean +GEN_MISC_FILES__QUOTED += "$(CFG_SRCDIR)/makefile.libs" + +#add generated source dir to list of directories to delete during a clean +#GEN_MISC_DIRS__QTD += "$(CFG_SRCDIR)" diff --git a/firmware/psu/platform/oc-sdr/cfg/OC_CONNECT1.c b/firmware/psu/platform/oc-sdr/cfg/OC_CONNECT1.c new file mode 100644 index 0000000000..cc51b4e5c4 --- /dev/null +++ b/firmware/psu/platform/oc-sdr/cfg/OC_CONNECT1.c @@ -0,0 +1,832 @@ +/******************************************************************************* + Filename: OC_CONNECT1.c + Revised: $Date: 2015-06-02 11:18:40 -0700 (Tue, 02 Jun 2015) $ + Revision: $Revision: 43957 $ + + Description: This file contains utility functions. + + Copyright 2014 Texas Instruments Incorporated. All rights reserved. + + IMPORTANT: Your use of this Software is limited to those specific rights + granted under the terms of a software license agreement between the user + who downloaded the software, his/her employer (which must be your employer) + and Texas Instruments Incorporated (the "License"). You may not use this + Software unless you agree to abide by the terms of the License. The License + limits your use, and you acknowledge, that the Software may not be modified, + copied or distributed unless embedded on a Texas Instruments microcontroller + or used solely and exclusively in conjunction with a Texas Instruments radio + frequency transceiver, which is integrated into your product. Other than for + the foregoing purpose, you may not use, reproduce, copy, prepare derivative + works of, modify, distribute, perform, display or sell this Software and/or + its documentation for any purpose. + + YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE + PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, + INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE, + NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL + TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT, + NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER + LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES + INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE + OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT + OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES + (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS. + + Should you have any questions regarding your right to use this Software, + contact Texas Instruments Incorporated at www.TI.com. +*******************************************************************************/ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef TI_DRIVERS_UART_DMA +#define TI_DRIVERS_UART_DMA 0 +#endif + +#ifndef TI_EXAMPLES_PPP +#define TI_EXAMPLES_PPP 0 +#else +/* prototype for NIMU init function */ +extern int USBSerialPPP_NIMUInit(); +#endif + +#include + + +typedef enum DK_TM4C129X_UARTName { + DK_TM4C129X_UART0 = 0, + DK_TM4C129X_UART1, + DK_TM4C129X_UART2, + DK_TM4C129X_UART3, + DK_TM4C129X_UART4, + DK_TM4C129X_UARTCOUNT +} DK_TM4C129X_UARTName; + +UARTTiva_Object uartTivaObjects[DK_TM4C129X_UARTCOUNT]; +unsigned char uartTivaRingBuffer[DK_TM4C129X_UARTCOUNT][32]; + +const UARTTiva_HWAttrs uartTivaHWAttrs[DK_TM4C129X_UARTCOUNT] = { + { + .baseAddr = UART0_BASE, + .intNum = INT_UART0, + .intPriority = (~0), + .flowControl = UART_FLOWCONTROL_NONE, + .ringBufPtr = uartTivaRingBuffer[0], + .ringBufSize = sizeof(uartTivaRingBuffer[0]) + }, + { + .baseAddr = UART1_BASE, + .intNum = INT_UART1, + .intPriority = (~0), + .flowControl = UART_FLOWCONTROL_NONE, + .ringBufPtr = uartTivaRingBuffer[1], + .ringBufSize = sizeof(uartTivaRingBuffer[1]) + }, + { + .baseAddr = UART2_BASE, + .intNum = INT_UART2, + .intPriority = (~0), + .flowControl = UART_FLOWCONTROL_NONE, + .ringBufPtr = uartTivaRingBuffer[2], + .ringBufSize = sizeof(uartTivaRingBuffer[2]) + }, + { + .baseAddr = UART4_BASE, + .intNum = INT_UART3, + .intPriority = (~0), + .flowControl = UART_FLOWCONTROL_NONE, + .ringBufPtr = uartTivaRingBuffer[3], + .ringBufSize = sizeof(uartTivaRingBuffer[3]) + }, + { + .baseAddr = UART4_BASE, + .intNum = INT_UART4, + .intPriority = (~0), + .flowControl = UART_FLOWCONTROL_NONE, + .ringBufPtr = uartTivaRingBuffer[4], + .ringBufSize = sizeof(uartTivaRingBuffer[4]) + }, + { + .baseAddr = UART5_BASE, + .intNum = INT_UART5, + .intPriority = (~0), + .flowControl = UART_FLOWCONTROL_NONE, + .ringBufPtr = uartTivaRingBuffer[5], + .ringBufSize = sizeof(uartTivaRingBuffer[5]) + } +}; + +const UART_Config UART_config[] = { + { + .fxnTablePtr = &UARTTiva_fxnTable, + .object = &uartTivaObjects[0], + .hwAttrs = &uartTivaHWAttrs[0] + }, + { + .fxnTablePtr = &UARTTiva_fxnTable, + .object = &uartTivaObjects[1], + .hwAttrs = &uartTivaHWAttrs[1] + }, + { + .fxnTablePtr = &UARTTiva_fxnTable, + .object = &uartTivaObjects[2], + .hwAttrs = &uartTivaHWAttrs[2] + }, + { + .fxnTablePtr = &UARTTiva_fxnTable, + .object = &uartTivaObjects[3], + .hwAttrs = &uartTivaHWAttrs[3] + }, + { + .fxnTablePtr = &UARTTiva_fxnTable, + .object = &uartTivaObjects[4], + .hwAttrs = &uartTivaHWAttrs[4] + }, + {NULL, NULL, NULL} +}; + +#include "common/inc/global/ocmp_frame.h" +#include "inc/common/global_header.h" +char input[64]; +UART_Handle uart; + +Void echoFxn(UArg arg0, UArg arg1) +{ + // char input; + UART_Params uartParams; + const char echoPrompt[] = "\fEchoing characters:\r\n"; + + OC_CONNECT1_initUART(); + + /* Create a UART with data processing off. */ + UART_Params_init(&uartParams); + uartParams.writeDataMode = UART_DATA_BINARY; + uartParams.readDataMode = UART_DATA_BINARY; + uartParams.readReturnMode = UART_RETURN_FULL; + uartParams.readEcho = UART_ECHO_OFF; + uartParams.baudRate = 115200; + uart = UART_open(DK_TM4C129X_UART4, &uartParams); + + if (uart == NULL) { + System_abort("Error opening the UART"); + } + + // UART_write(uart, echoPrompt, sizeof(echoPrompt)); + + /* Loop forever echoing */ + // input = '1'; + while (1) { + UART_read(uart, &input, 64); + uint8_t * pWrite = NULL; + pWrite = (uint8_t *) malloc( + sizeof(OCMPMessageFrame) + OCMP_FRAME_MSG_LENGTH); + if (pWrite != NULL) { + memset(pWrite, '\0', 64); + memcpy(pWrite, input, 64); +#if 1 + uint8_t i = 0; + LOGGER_DEBUG("UARTDMACTR:INFO:: UART RX BUFFER:\n"); + for( i = 0; i < 64; i++) + { + LOGGER_DEBUG("0x%x ",pWrite[i]); + } + LOGGER_DEBUG("\n"); + } +#endif + // UART_write(uart, &input, 1); + } +} + +/* + * =============================== DMA =============================== + */ +#if defined(__TI_COMPILER_VERSION__) +#pragma DATA_ALIGN(dmaControlTable, 1024) +#elif defined(__IAR_SYSTEMS_ICC__) +#pragma data_alignment=1024 +#elif defined(__GNUC__) +__attribute__ ((aligned (1024))) +#endif +static tDMAControlTable dmaControlTable[32]; +static bool dmaInitialized = false; + +/* Hwi_Struct used in the initDMA Hwi_construct call */ +static Hwi_Struct dmaHwiStruct; + +/* Hwi_Struct used in the usbBusFault Hwi_construct call */ +static Hwi_Struct usbBusFaultHwiStruct; + +/* + * ======== dmaErrorHwi ======== + */ +static void dmaErrorHwi(UArg arg) +{ + System_printf("DMA error code: %d\n", uDMAErrorStatusGet()); + uDMAErrorStatusClear(); + System_abort("DMA error!!"); +} + +/* + * ======== OC_CONNECT1_initDMA ======== + */ +void OC_CONNECT1_initDMA(void) +{ + Error_Block eb; + Hwi_Params hwiParams; + + if (!dmaInitialized) { + Error_init(&eb); + Hwi_Params_init(&hwiParams); + Hwi_construct(&(dmaHwiStruct), INT_UDMAERR, dmaErrorHwi, &hwiParams, + &eb); + if (Error_check(&eb)) { + System_abort("Couldn't construct DMA error hwi"); + } + + SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA); + uDMAEnable(); + uDMAControlBaseSet(dmaControlTable); + + dmaInitialized = true; + } +} + +/* + * =============================== General =============================== + */ +/* + * ======== OC_CONNECT1_initGeneral ======== + */ +void OC_CONNECT1_initGeneral(void) +{ + SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); + SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB); + SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC); + SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD); + SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE); + SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); + SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG); +} +#if defined(__TI_COMPILER_VERSION__) +#pragma DATA_SECTION(EMAC_config, ".const:EMAC_config") +#pragma DATA_SECTION(emacHWAttrs, ".const:emacHWAttrs") +#pragma DATA_SECTION(NIMUDeviceTable, ".data:NIMUDeviceTable") +#endif + +#include +#include + +/* + * Required by the Networking Stack (NDK). This array must be NULL terminated. + * This can be removed if NDK is not used. + * Double curly braces are needed to avoid GCC bug #944572 + * https://bugs.launchpad.net/gcc-linaro/+bug/944572 + */ +NIMU_DEVICE_TABLE_ENTRY NIMUDeviceTable[2] = { + { +#if TI_EXAMPLES_PPP + /* Use PPP driver for PPP example only */ + .init = USBSerialPPP_NIMUInit +#else + /* Default: use Ethernet driver */ + .init = EMACSnow_NIMUInit +#endif + }, + { NULL } +}; + +EMACSnow_Object emacObjects[OC_CONNECT1_EMACCOUNT]; + +/* + * EMAC configuration structure + * Set user/company specific MAC octates. The following sets the address + * to ff-ff-ff-ff-ff-ff. Users need to change this to make the label on + * their boards. + */ +unsigned char macAddress[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +const EMACSnow_HWAttrs emacHWAttrs[OC_CONNECT1_EMACCOUNT] = { + [OC_CONNECT1_EMAC0] = { + .baseAddr = EMAC0_BASE, + // .intNum = INT_EMAC0, + .intPriority = (~0), + .macAddress = macAddress + } +}; + +const EMAC_Config EMAC_config[] = { + [OC_CONNECT1_EMAC0] = { + .fxnTablePtr = &EMACSnow_fxnTable, + .object = &emacObjects[OC_CONNECT1_EMAC0], + .hwAttrs = &emacHWAttrs[OC_CONNECT1_EMAC0] + }, + { NULL, NULL, NULL } +}; + +/* + * ======== OC_CONNECT1_initEMAC ======== + */ +void OC_CONNECT1_initEMAC(void) +{ + uint32_t ulUser0, ulUser1; + + /* Get the MAC address */ + FlashUserGet(&ulUser0, &ulUser1); + if ((ulUser0 != 0xffffffff) && (ulUser1 != 0xffffffff)) { + System_printf("Using MAC address in flash\n"); + /* + * Convert the 24/24 split MAC address from NV ram into a 32/16 split + * MAC address needed to program the hardware registers, then program + * the MAC address into the Ethernet Controller registers. + */ + macAddress[0] = ((ulUser0 >> 0) & 0xff); + macAddress[1] = ((ulUser0 >> 8) & 0xff); + macAddress[2] = ((ulUser0 >> 16) & 0xff); + macAddress[3] = ((ulUser1 >> 0) & 0xff); + macAddress[4] = ((ulUser1 >> 8) & 0xff); + macAddress[5] = ((ulUser1 >> 16) & 0xff); + } else if (macAddress[0] == 0xff && macAddress[1] == 0xff + && macAddress[2] == 0xff && macAddress[3] == 0xff + && macAddress[4] == 0xff && macAddress[5] == 0xff) { + System_printf("Change the macAddress variable to valid Mac address"); + } + +// GPIOPinConfigure(GPIO_PF0_EN0LED0); /* OC_CONNECT1_USR_D3 */ +// GPIOPinConfigure(GPIO_PF4_EN0LED1); /* OC_CONNECT1_USR_D4 */ +// GPIOPinTypeEthernetLED(GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_4); + + /* Once EMAC_init is called, EMAC_config cannot be changed */ + EMAC_init(); +} + +/* + * =============================== GPIO =============================== + */ +/* Place into subsections to allow the TI linker to remove items properly */ +#if defined(__TI_COMPILER_VERSION__) +#pragma DATA_SECTION(GPIOTiva_config, ".const:GPIOTiva_config") +#endif + +#include +#include +extern GPIO_PinConfig gpioPinConfigs[]; +/* + * Array of Pin configurations + * NOTE: The order of the pin configurations must coincide with what was + * defined in OC_CONNECT1.h + * NOTE: Pins not used for interrupts should be placed at the end of the + * array. Callback entries can be omitted from callbacks array to + * reduce memory usage. + */ +GPIO_PinConfig gpioPinConfigs[OC_EC_GPIOCOUNT] = { + [OC_EC_PSE_INT] = + GPIOTiva_PA_0 | GPIO_CFG_INPUT | GPIO_CFG_IN_INT_FALLING, + [OC_EC_nPSE_RESET] = + GPIOTiva_PA_1 | GPIO_CFG_OUT_STD | GPIO_CFG_OUT_STR_HIGH | GPIO_CFG_OUT_LOW, + [OC_EC_WD_INPUT] = + GPIOTiva_PA_2 | GPIO_CFG_OUT_STD | GPIO_CFG_OUT_STR_HIGH | GPIO_CFG_OUT_HIGH, + [OC_EC_ENABLE_OC_INPUT] = + GPIOTiva_PA_3 | GPIO_CFG_OUT_STD | GPIO_CFG_OUT_STR_HIGH | GPIO_CFG_OUT_LOW, + [OC_EC_POWER_OFF] = + GPIOTiva_PA_4 | GPIO_CFG_OUT_STD | GPIO_CFG_OUT_STR_HIGH | GPIO_CFG_OUT_LOW, + [OC_EC_DISABLE_DC_INPUT] = + GPIOTiva_PB_0 | GPIO_CFG_OUT_STD | GPIO_CFG_OUT_STR_HIGH | GPIO_CFG_OUT_LOW, + [OC_EC_IOEXP_INT] = + GPIOTiva_PB_1 | GPIO_CFG_INPUT | GPIO_CFG_IN_INT_FALLING, + [OC_EC_ENABLE_PASSIVE_POE] = + GPIOTiva_PB_4 | GPIO_CFG_OUTPUT | GPIO_CFG_OUT_LOW, + [OC_EC_CS_ALERT_24V_20V] = + GPIOTiva_PB_5 | GPIO_CFG_IN_PU , + [OC_EC_EN_INT_BATT_PWR] = + GPIOTiva_PC_7 | GPIO_CFG_IN_NOPULL | GPIO_CFG_IN_INT_BOTH_EDGES, + [OC_EC_DC_INPUT_FAULT] = + GPIOTiva_PC_6 | GPIO_CFG_INPUT | GPIO_CFG_IN_INT_BOTH_EDGES, + [OC_EC_UART_TX] = + GPIOTiva_PC_5 | GPIO_CFG_OUTPUT, + [OC_EC_CS_ALERT] = + GPIOTiva_PD_0 | GPIO_CFG_INPUT , + [OC_EC_DC_IN_PRESENT] = + GPIOTiva_PD_1 | GPIO_CFG_IN_NOPULL | GPIO_CFG_IN_INT_FALLING, + [OC_EC_CS_ALERT_12V_GBC] = + GPIOTiva_PD_2 | GPIO_CFG_IN_NOPULL | GPIO_CFG_IN_INT_FALLING, + [OC_EC_CS_ALERT_12V_BB] = + GPIOTiva_PD_3 | GPIO_CFG_IN_NOPULL | GPIO_CFG_IN_INT_BOTH_EDGES, + [OC_EC_CS_ALERT_12V_FE] = + GPIOTiva_PD_4 | GPIO_CFG_IN_PU , + [OC_EC_PGOOD_5V0] = + GPIOTiva_PD_5 | GPIO_CFG_IN_NOPULL, + [OC_EC_PGOOD_12V0] = + GPIOTiva_PD_6 | GPIO_CFG_INPUT, + [OC_EC_IVINMON] = + GPIOTiva_PE_0 | GPIO_CFG_IN_NOPULL, + [OC_EC_ISMON] = + GPIOTiva_PE_1 | GPIO_CFG_IN_NOPULL, + [OC_EC_CHARGER_ALERT] = + GPIOTiva_PE_2 | GPIO_CFG_IN_NOPULL | GPIO_CFG_IN_INT_FALLING, + [OC_EC_PGOOD_BOOST_CONV_BATT] = + GPIOTiva_PE_3 | GPIO_CFG_IN_NOPULL , + [OC_EC_TIVA_GPIO1] = + GPIOTiva_PF_0 | GPIO_CFG_IN_NOPULL | GPIO_CFG_IN_INT_BOTH_EDGES, + [OC_EC_TIVA_GPIO2] = + GPIOTiva_PF_1 | GPIO_CFG_IN_NOPULL | GPIO_CFG_IN_INT_FALLING, + [OC_EC_BUZZER_ON] = + GPIOTiva_PF_2 | GPIO_CFG_OUTPUT | GPIO_CFG_OUT_LOW, + [OC_EC_PD_T2P] = + GPIOTiva_PF_3 | GPIO_CFG_INPUT , + [OC_EC_TEMP_EVENT] = + GPIOTiva_PF_4 | GPIO_CFG_IN_NOPULL, + [OC_EC_OC_IN_PRESENT] = + GPIOTiva_PG_4 | GPIO_CFG_INPUT, + [OC_EC_POE_IN_PRESENT] = + GPIOTiva_PG_5 | GPIO_CFG_INPUT, +}; + +/* + * Array of callback function pointers + * NOTE: The order of the pin configurations must coincide with what was + * defined in OC_CONNECT1.h + * NOTE: Pins not used for interrupts can be omitted from callbacks array to + * reduce memory usage (if placed at end of gpioPinConfigs array). + * We have lots of RAM right now, so just set it to full size of + * GPIO array + */ +GPIO_CallbackFxn gpioCallbackFunctions[OC_EC_GPIOCOUNT] = { }; + +/* The device-specific GPIO_config structure */ +const GPIOTiva_Config GPIOTiva_config = { + .pinConfigs = (GPIO_PinConfig *) gpioPinConfigs, + .callbacks = (GPIO_CallbackFxn *) gpioCallbackFunctions, + .numberOfPinConfigs = sizeof(gpioPinConfigs) / sizeof(GPIO_PinConfig), + .numberOfCallbacks = sizeof(gpioCallbackFunctions) / + sizeof(GPIO_CallbackFxn), + .intPriority = (~0) +}; + +/* Below is a sample system config demonstrating how we can configure our + * subsystems. It will effectively replace board.h. I've kept the header + * includes here since I'm thinking we'll want to move this out at some point + */ +#include "common/inc/global/ocmp_frame.h" +#include "drivers/GpioSX1509.h" +#include "drivers/GpioNative.h" + +OcGpio_Port ec_io = { + .fn_table = &GpioNative_fnTable, +}; + +OcGpio_Port pwr_io = { + .fn_table = &GpioSX1509_fnTable, + .cfg = &(SX1509_Cfg) { + .i2c_dev = { OC_CONNECT1_I2C5, 0x3E }, + // .pin_irq = &(OcGpio_Pin){ &ec_io, OC_EC_IOEXP_INT}, + }, + .object_data = &(SX1509_Obj){}, +}; + +/* + * ======== OC_CONNECT1_initGPIO ======== + */ +void OC_CONNECT1_initGPIO(void) +{ + /* Initialize peripheral and pins */ + GPIO_init(); + + GpioNative_init(); +} + +/* + * =============================== I2C =============================== + */ +/* Place into subsections to allow the TI linker to remove items properly */ +#if defined(__TI_COMPILER_VERSION__) +#pragma DATA_SECTION(I2C_config, ".const:I2C_config") +#pragma DATA_SECTION(i2cTivaHWAttrs, ".const:i2cTivaHWAttrs") +#endif + +#include +#include + +I2CTiva_Object i2cTivaObjects[OC_CONNECT1_I2CCOUNT]; + +const I2CTiva_HWAttrs i2cTivaHWAttrs[OC_CONNECT1_I2CCOUNT] = { + [OC_CONNECT1_I2C0] = { + .baseAddr = I2C0_BASE, + .intNum = INT_I2C0, + .intPriority = (~0) + }, + [OC_CONNECT1_I2C1] = { + .baseAddr = I2C1_BASE, + .intNum = INT_I2C1, + .intPriority = (~0) + }, + [OC_CONNECT1_I2C2] = { + .baseAddr = I2C2_BASE, + .intNum = INT_I2C2, + .intPriority = (~0) + }, + [OC_CONNECT1_I2C3] = { + .baseAddr = I2C3_BASE, + .intNum = INT_I2C3, + .intPriority = (~0) + }, + [OC_CONNECT1_I2C4] = { + .baseAddr = I2C4_BASE, + .intNum = INT_I2C4, + .intPriority = (~0) + }, + [OC_CONNECT1_I2C5] = { + .baseAddr = I2C5_BASE, + .intNum = INT_I2C5, + .intPriority = (~0) + }, +}; + +const I2C_Config I2C_config[] = { + [OC_CONNECT1_I2C0] = { + .fxnTablePtr = &I2CTiva_fxnTable, + .object = &i2cTivaObjects[OC_CONNECT1_I2C0], + .hwAttrs = &i2cTivaHWAttrs[OC_CONNECT1_I2C0] + }, + [OC_CONNECT1_I2C1] = { + .fxnTablePtr = &I2CTiva_fxnTable, + .object = &i2cTivaObjects[OC_CONNECT1_I2C1], + .hwAttrs = &i2cTivaHWAttrs[OC_CONNECT1_I2C1] + }, + [OC_CONNECT1_I2C2] = { + .fxnTablePtr = &I2CTiva_fxnTable, + .object = &i2cTivaObjects[OC_CONNECT1_I2C2], + .hwAttrs = &i2cTivaHWAttrs[OC_CONNECT1_I2C2] + }, + [OC_CONNECT1_I2C3] = { + .fxnTablePtr = &I2CTiva_fxnTable, + .object = &i2cTivaObjects[OC_CONNECT1_I2C3], + .hwAttrs = &i2cTivaHWAttrs[OC_CONNECT1_I2C3] + }, + [OC_CONNECT1_I2C4] = { + .fxnTablePtr = &I2CTiva_fxnTable, + .object = &i2cTivaObjects[OC_CONNECT1_I2C4], + .hwAttrs = &i2cTivaHWAttrs[OC_CONNECT1_I2C4] + }, + [OC_CONNECT1_I2C5] = { + .fxnTablePtr = &I2CTiva_fxnTable, + .object = &i2cTivaObjects[OC_CONNECT1_I2C5], + .hwAttrs = &i2cTivaHWAttrs[OC_CONNECT1_I2C5] + }, + { NULL, NULL, NULL } +}; + +/* + * ======== OC_CONNECT1_initI2C ======== + */ +void OC_CONNECT1_initI2C(void) +{ + + //Remove I2C0 PWR2 + /* I2C0 Init */ + /* Enable the peripheral */ + SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0); + + /* Configure the appropriate pins to be I2C instead of GPIO. */ + GPIOPinConfigure(GPIO_PB2_I2C0SCL); + GPIOPinConfigure(GPIO_PB3_I2C0SDA); + GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2); + GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3); + + /* I2C1 Init */ + /* Enable the peripheral */ + SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C1); + + /* Configure the appropriate pins to be I2C instead of GPIO. */ + GPIOPinConfigure(GPIO_PA6_I2C1SCL); + GPIOPinConfigure(GPIO_PA7_I2C1SDA); + GPIOPinTypeI2CSCL(GPIO_PORTA_BASE, GPIO_PIN_6); + GPIOPinTypeI2C(GPIO_PORTA_BASE, GPIO_PIN_7); + + /* I2C2 Init */ + /* Enable the peripheral */ + SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C2); + + /* Configure the appropriate pins to be I2C instead of GPIO. */ + GPIOPinConfigure(GPIO_PE4_I2C2SCL); + GPIOPinConfigure(GPIO_PE5_I2C2SDA); + GPIOPinTypeI2CSCL(GPIO_PORTE_BASE, GPIO_PIN_4); + GPIOPinTypeI2C(GPIO_PORTE_BASE, GPIO_PIN_5); + + /* I2C3 Init */ + /* Enable the peripheral */ + SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C3); + + /* Configure the appropriate pins to be I2C instead of GPIO. */ + GPIOPinConfigure(GPIO_PG0_I2C3SCL); + GPIOPinConfigure(GPIO_PG1_I2C3SDA); + GPIOPinTypeI2CSCL(GPIO_PORTG_BASE, GPIO_PIN_0); + GPIOPinTypeI2C(GPIO_PORTG_BASE, GPIO_PIN_1); + + /* I2C4 Init */ + /* Enable the peripheral */ + SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C4); + + /* Configure the appropriate pins to be I2C instead of GPIO. */ + GPIOPinConfigure(GPIO_PG2_I2C4SCL); + GPIOPinConfigure(GPIO_PG3_I2C4SDA); + GPIOPinTypeI2CSCL(GPIO_PORTG_BASE, GPIO_PIN_2); + GPIOPinTypeI2C(GPIO_PORTG_BASE, GPIO_PIN_3); + + /* I2C5 Init */ + /* Enable the peripheral */ + SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C5); + + /* Configure the appropriate pins to be I2C instead of GPIO. */ + GPIOPinConfigure(GPIO_PB6_I2C5SCL); + GPIOPinConfigure(GPIO_PB7_I2C5SDA); + GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_6); + GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_7); + + I2C_init(); +} + +/* + * =============================== UART =============================== + */ + +/* Place into subsections to allow the TI linker to remove items properly */ +#if defined(__TI_COMPILER_VERSION__) +#pragma DATA_SECTION(UART_config, ".const:UART_config") +#pragma DATA_SECTION(uartTivaHWAttrs, ".const:uartTivaHWAttrs") +#endif + +#include +/* ======== OC_CONNECT1_initUART ======== + */ +void OC_CONNECT1_initUART(void) +{ + + // UART For external communication + SysCtlPeripheralEnable(SYSCTL_PERIPH_UART4); + GPIOPinConfigure(GPIO_PC4_U4RX); + GPIOPinConfigure(GPIO_PC5_U4TX); + GPIOPinTypeUART(GPIO_PORTC_BASE, GPIO_PIN_4 | GPIO_PIN_5); + + // SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1); + // GPIOPinConfigure(GPIO_PC4_U1RX); + // GPIOPinConfigure(GPIO_PC5_U1TX); + // GPIOPinTypeUART(GPIO_PORTC_BASE, GPIO_PIN_4 | GPIO_PIN_5); + + /* Initialize the UART driver */ +#if TI_DRIVERS_UART_DMA + OC_CONNECT1_initDMA(); +#endif + UART_init(); +} + +/* + * =============================== USB =============================== + */ +/* + * ======== OC_CONNECT1_usbBusFaultHwi ======== + */ +#if 0 +static void OC_CONNECT1_usbBusFaultHwi(UArg arg) +{ + /* + * This function should be modified to appropriately manage handle + * a USB bus fault. + */ + System_printf("USB bus fault detected."); + Hwi_clearInterrupt(INT_GPIOQ4); + System_abort("USB error!!"); +} + +/* + * ======== OC_CONNECT1_initUSB ======== + * This function just turns on the USB + */ + +void OC_CONNECT1_initUSB(OC_CONNECT1_USBMode usbMode) +{ +//PWR2 + Error_Block eb; + Hwi_Params hwiParams; + + /* Enable the USB peripheral and PLL */ + SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0); + SysCtlUSBPLLEnable(); + + /* Setup pins for USB operation */ + GPIOPinTypeUSBAnalog(GPIO_PORTB_BASE, GPIO_PIN_0 | GPIO_PIN_1); + GPIOPinTypeUSBAnalog(GPIO_PORTL_BASE, GPIO_PIN_6 | GPIO_PIN_7); + + /* Additional configurations for Host mode */ + if (usbMode == OC_CONNECT1_USBHOST) { + /* Configure the pins needed */ + HWREG(GPIO_PORTD_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY; + HWREG(GPIO_PORTD_BASE + GPIO_O_CR) = 0xff; + GPIOPinConfigure(GPIO_PD6_USB0EPEN); + GPIOPinTypeUSBDigital(GPIO_PORTD_BASE, GPIO_PIN_6 | GPIO_PIN_7); + + /* + * USB bus fault is routed to pin PQ4. We create a Hwi to allow us + * to detect power faults and recover gracefully or terminate the + * program. PQ4 is active low; set the pin as input with a weak + * pull-up. + */ + GPIOPadConfigSet(GPIO_PORTQ_BASE, GPIO_PIN_4, + GPIO_STRENGTH_2MA, + GPIO_PIN_TYPE_STD_WPU); + GPIOIntTypeSet(GPIO_PORTQ_BASE, GPIO_PIN_4, GPIO_FALLING_EDGE); + GPIOIntClear(GPIO_PORTQ_BASE, GPIO_PIN_4); + + /* Create a Hwi for PQ4 pin. */ + Error_init(&eb); + Hwi_Params_init(&hwiParams); + Hwi_construct(&(usbBusFaultHwiStruct), INT_GPIOQ4, + OC_CONNECT1_usbBusFaultHwi, &hwiParams, &eb); + if (Error_check(&eb)) { + System_abort("Couldn't construct USB bus fault hwi"); + } + } +} +#endif + + +/* + * =============================== Watchdog =============================== + */ +/* Place into subsections to allow the TI linker to remove items properly */ +#if defined(__TI_COMPILER_VERSION__) +#pragma DATA_SECTION(Watchdog_config, ".const:Watchdog_config") +#pragma DATA_SECTION(watchdogTivaHWAttrs, ".const:watchdogTivaHWAttrs") +#endif + +#include +#include + +WatchdogTiva_Object watchdogTivaObjects[OC_CONNECT1_WATCHDOGCOUNT]; + +const WatchdogTiva_HWAttrs watchdogTivaHWAttrs[OC_CONNECT1_WATCHDOGCOUNT] = { + [OC_CONNECT1_WATCHDOG0] = { + .baseAddr = WATCHDOG0_BASE, + .intNum = INT_WATCHDOG, + .intPriority = (~0), + .reloadValue = 80000000 // 1 second period at default CPU clock freq + }, +}; + +const Watchdog_Config Watchdog_config[] = { + [OC_CONNECT1_WATCHDOG0] = { + .fxnTablePtr = &WatchdogTiva_fxnTable, + .object = &watchdogTivaObjects[OC_CONNECT1_WATCHDOG0], + .hwAttrs = &watchdogTivaHWAttrs[OC_CONNECT1_WATCHDOG0] + }, + { NULL, NULL, NULL }, +}; + +/* + * ======== OC_CONNECT1_initWatchdog ======== + * + * NOTE: To use the other watchdog timer with base address WATCHDOG1_BASE, + * an additional function call may need be made to enable PIOSC. Enabling + * WDOG1 does not do this. Enabling another peripheral that uses PIOSC + * such as ADC0 or SSI0, however, will do so. Example: + * + * SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0); + * SysCtlPeripheralEnable(SYSCTL_PERIPH_WDOG1); + * + * See the following forum post for more information: + * http://e2e.ti.com/support/microcontrollers/stellaris_arm_cortex-m3_microcontroller/f/471/p/176487/654390.aspx#654390 + */ +void OC_CONNECT1_initWatchdog(void) +{ + /* Enable peripherals used by Watchdog */ + SysCtlPeripheralEnable(SYSCTL_PERIPH_WDOG0); + + Watchdog_init(); +} diff --git a/firmware/psu/platform/oc-sdr/cfg/OC_CONNECT_PSU.c b/firmware/psu/platform/oc-sdr/cfg/OC_CONNECT_PSU.c new file mode 100644 index 0000000000..d1de86560c --- /dev/null +++ b/firmware/psu/platform/oc-sdr/cfg/OC_CONNECT_PSU.c @@ -0,0 +1,422 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "common/inc/global/ocmp_frame.h" +#include "common/inc/global/OC_CONNECT1.h" +#include "common/inc/ocmp_wrappers/ocmp_debugi2c.h" +#include "common/inc/ocmp_wrappers/ocmp_ina226.h" +#include "common/inc/ocmp_wrappers/ocmp_ltc4015.h" +#include "common/inc/ocmp_wrappers/ocmp_ltc4274.h" +#include "common/inc/ocmp_wrappers/ocmp_se98a.h" +#include "drivers/GpioSX1509.h" +#include "inc/devices/debug_ocgpio.h" +#include "inc/devices/debug_oci2c.h" +#include "inc/devices/eeprom.h" +#include "inc/devices/ina226.h" +#include "inc/devices/int_battery.h" +#include "inc/subsystem/hci/hci.h" +#include "inc/subsystem/power/power.h" +#include +#include + +SCHEMA_IMPORT OcGpio_Port ec_io; +SCHEMA_IMPORT OcGpio_Port pwr_io; +SCHEMA_IMPORT const Driver_fxnTable LTC4274_fxnTable; + +/***************************************************************************** + * EEPROM CONFIG + *****************************************************************************/ + +Eeprom_Cfg eeprom_psu_sid = { + .i2c_dev = { OC_CONNECT1_I2C0, 0x56 }, +// .pin_wp = &pin_s_id_eeprom_wp, + .type = CAT24C256, +// .ss = OC_SS_SYS, +}; + +Eeprom_Cfg eeprom_psu_inv = { + .i2c_dev = { OC_CONNECT1_I2C5, 0x56 }, +// .pin_wp = &pin_inven_eeprom_wp, + .type = CAT24C256, +// .ss = OC_SS_SYS, +}; + +/***************************************************************************** + * SYSTEM CONFIG + *****************************************************************************/ +/* Power SubSystem Config */ +//Lithium ion battery charge controller. +LTC4015_Dev psu_pwr_int_bat_charger = { + .cfg = { + .i2c_dev = { + .bus = OC_CONNECT1_I2C2, + .slave_addr = 0x68, /* LTC4015 I2C address in 7-bit format */ + }, + .chem = LTC4015_CHEM_LI_ION, + .r_snsb = PWR_INT_BATT_RSNSB, + .r_snsi = PWR_INT_BATT_RSNSI, + .cellcount = 3, + .pin_alert = &(OcGpio_Pin){ &ec_io, OC_EC_CHARGER_ALERT}, + }, + .obj = {}, +}; + +INA226_Dev pwr_bat_ps_12v = { + /* 12V Power Sensor */ + .cfg = { + .dev = { + .bus = OC_CONNECT1_I2C3, + .slave_addr = 0x58, + }, + .pin_alert = &(OcGpio_Pin){ &ec_io, + OC_EC_CHARGER_ALERT }, + }, +}; + +INA226_Dev psu_fe_ps_1v = { + /* 12V Power Sensor */ + .cfg = { + .dev = { + .bus = OC_CONNECT1_I2C4, + .slave_addr = 0x45, + }, + .pin_alert = &(OcGpio_Pin){ &ec_io, + OC_EC_CHARGER_ALERT }, + }, +}; + +INA226_Dev psu_fe_ps_3v = { + /* 12V Power Sensor */ + .cfg = { + .dev = { + .bus = OC_CONNECT1_I2C4, + .slave_addr = 0x44, + }, + .pin_alert = &(OcGpio_Pin){ &ec_io, + OC_EC_CHARGER_ALERT }, + }, +}; + +INA226_Dev psu_fe_ps_5v = { + /* 12V Power Sensor */ + .cfg = { + .dev = { + .bus = OC_CONNECT1_I2C4, + .slave_addr = 0x41, + }, + .pin_alert = &(OcGpio_Pin){ &ec_io, + OC_EC_CHARGER_ALERT }, + }, +}; + +INA226_Dev psu_fe_ps_12v = { + /* 12V Power Sensor */ + .cfg = { + .dev = { + .bus = OC_CONNECT1_I2C1, + .slave_addr = 0x41, + }, + .pin_alert = &(OcGpio_Pin){ &ec_io, + OC_EC_CHARGER_ALERT }, + }, +}; + +INA226_Dev psu_fe_ps_24v = { + /* 12V Power Sensor */ + .cfg = { + .dev = { + .bus = OC_CONNECT1_I2C1, + .slave_addr = 0x45, + }, + .pin_alert = &(OcGpio_Pin){ &ec_io, + OC_EC_CHARGER_ALERT }, + }, +}; + +INA226_Dev psu_fe_ps_28v = { + /* 12V Power Sensor */ + .cfg = { + .dev = { + .bus = OC_CONNECT1_I2C4, + .slave_addr = 0x40, + }, + .pin_alert = &(OcGpio_Pin){ &ec_io, + OC_EC_CHARGER_ALERT }, + }, +}; + +INA226_Dev psu_gbc_ps_12v = { + /* 12V Power Sensor */ + .cfg = { + .dev = { + .bus = OC_CONNECT1_I2C1, + .slave_addr = 0x44, + }, + .pin_alert = &(OcGpio_Pin){ &ec_io, + OC_EC_CHARGER_ALERT }, + }, +}; +INA226_Dev psu_gen_ps_12v = { + /* 12V Power Sensor */ + .cfg = { + .dev = { + .bus = OC_CONNECT1_I2C2, + .slave_addr = 0x41, + }, + .pin_alert = &(OcGpio_Pin){ &ec_io, + OC_EC_CHARGER_ALERT }, + }, +}; + +INA226_Dev psu_gen_ps_24v = { + /* 12V Power Sensor */ + .cfg = { + .dev = { + .bus = OC_CONNECT1_I2C2, + .slave_addr = 0x44, + }, + .pin_alert = &(OcGpio_Pin){ &ec_io, + OC_EC_CHARGER_ALERT }, + }, +}; + +INA226_Dev psu_bb_ps_12v = { + /* 12V Power Sensor */ + .cfg = { + .dev = { + .bus = OC_CONNECT1_I2C1, + .slave_addr = 0x40, + }, + .pin_alert = &(OcGpio_Pin){ &ec_io, + OC_EC_CHARGER_ALERT }, + }, +}; +//Power Source Equipment +LTC4274_Dev psu_pwr_pse = { + .cfg = { + .i2c_dev = { + .bus = OC_CONNECT1_I2C3, + .slave_addr = 0x2F, /* LTC4274 I2C address in 7-bit format */ + }, + .pin_evt = &(OcGpio_Pin){ &ec_io, + OC_EC_PSE_INT }, + .reset_pin ={ &ec_io, OC_EC_nPSE_RESET}, + }, + .obj = {}, +}; + +//Power Device +LTC4295_Dev psu_pwr_pd = { + .cfg = { + //TODO: find the powergood pin + .pin_evt = &(OcGpio_Pin){ &ec_io, + OC_EC_POE_IN_PRESENT}, + .pin_detect = &(OcGpio_Pin){ &ec_io, + OC_EC_PD_T2P}, + }, + .obj = {}, + }; + +//Power Source +PWRSRC_Dev psu_pwr_powerSource = { /*Added as a place holder for now.*/ + .cfg = { + /* DC_POWER_PRESENT */ + .pin_dc_present = { &ec_io, OC_EC_DC_IN_PRESENT}, + /* POE_PRSNT_N */ + .pin_poe_prsnt_n = { &ec_io, OC_EC_POE_IN_PRESENT}, + /* INT_BAT_PRSNT */ + .pin_int_bat_prsnt = { &ec_io, OC_EC_EN_INT_BATT_PWR}, + + .pin_disable_dc_input = { &ec_io, OC_EC_DISABLE_DC_INPUT}, + .pin_dc_input_fault = { &ec_io, OC_EC_DC_INPUT_FAULT}, + .pin_oc_input_present = { &ec_io, OC_EC_OC_IN_PRESENT}, + .pin_power_off = { &ec_io, OC_EC_POWER_OFF}, + }, + .obj = {}, +}; +OcGpio_Pin pin_tempsen_evt1 = { &ec_io, OC_EC_TEMP_EVENT }; + +SE98A_Dev psu_sensor_ts1 = { + .cfg = { + .dev = { + .bus = OC_CONNECT1_I2C5, + .slave_addr = 0x18 + }, + .pin_evt = &pin_tempsen_evt1, + }, + .obj = {}, +}; + +SE98A_Dev psu_sensor_ts2 = { + .cfg = { + .dev = { + .bus = OC_CONNECT1_I2C5, + .slave_addr = 0x19, + }, + .pin_evt = &pin_tempsen_evt1, + }, + .obj = {}, +}; + +SE98A_Dev psu_sensor_ts3 = { + .cfg = { + .dev = { + .bus = OC_CONNECT1_I2C5, + .slave_addr = 0x1A, + }, + .pin_evt = &pin_tempsen_evt1, + }, + .obj = {}, +}; +/* HCI SubSystem Config */ +// Buzzer +HciBuzzer_Cfg psu_hci_buzzer = { + //.pin_en = { &, 10, OCGPIO_CFG_OUT_OD_NOPULL }, +}; + + +/* Debug Subsystem Config.*/ +//I2C Bus +S_I2C_Cfg debug_I2C0 = { + .bus = OC_CONNECT1_I2C0, +}; + +S_I2C_Cfg debug_I2C1 = { + .bus = OC_CONNECT1_I2C1, +}; + +S_I2C_Cfg debug_I2C2 = { + .bus = OC_CONNECT1_I2C2, +}; + +S_I2C_Cfg debug_I2C3 = { + .bus = OC_CONNECT1_I2C3, +}; + +S_I2C_Cfg debug_I2C4 = { + .bus = OC_CONNECT1_I2C4, +}; + +S_I2C_Cfg debug_I2C5 = { + .bus = OC_CONNECT1_I2C5, +}; + +//Native GPIO +S_OCGPIO_Cfg debug_psu_gpio_pa = { + .port = &ec_io, + .group = PA, +}; + +S_OCGPIO_Cfg debug_psu_gpio_pb = { + .port = &ec_io, + .group = PB, +}; + +S_OCGPIO_Cfg debug_psu_gpio_pc = { + .port = &ec_io, + .group = PC, +}; + +S_OCGPIO_Cfg debug_psu_gpio_pd = { + .port = &ec_io, + .group = PD, +}; + +S_OCGPIO_Cfg debug_psu_gpio_pe = { + .port = &ec_io, + .group = PE, +}; + +S_OCGPIO_Cfg debug_psu_gpio_pf = { + .port = &ec_io, + .group = PF, +}; + +S_OCGPIO_Cfg debug_psu_gpio_pg = { + .port = &ec_io, + .group = PG, +}; + +// IO EXPANDERS +S_OCGPIO_Cfg debug_psu_ioexpander = { + .port = &pwr_io, +}; + +/* Factory Configuration for the Devices*/ +//Power Factory Config. +const SE98A_Config fact_bc_se98a = { + .lowlimit = -20, + .highlimit = 75, + .critlimit = 80, +}; + +const LTC4015_Config fact_lithiumIon_cfg = { + .batteryVoltageLow = 9500, + .batteryVoltageHigh = 12600, + .batteryCurrentLow = 100, + .inputVoltageLow = 16200, + .inputCurrentHigh = 5000, + .inputCurrentLimit = 5570, +}; + +const LTC4274_Config fact_ltc4274_cfg = { + .operatingMode = LTC4274_AUTO_MODE, + .detectEnable = LTC4274_DETECT_ENABLE, + .interruptMask = LTC4274_INTERRUPT_MASK, + .interruptEnable = true, + .pseHpEnable = LTC4274_HP_ENABLE, +}; + +//BMS factory config. +const SE98A_Config fact_ec_se98a_cfg = { + .lowlimit = -20, + .highlimit = 75, + .critlimit = 80, +}; + +const INA226_Config fact_lion_12v_ps_cfg = { + .current_lim = 1000, +}; +const INA226_Config fact_fe_1v_ps_cfg = { + .current_lim = 1000, +}; +const INA226_Config fact_fe_3v_ps_cfg = { + .current_lim = 1000, +}; +const INA226_Config fact_fe_5v_ps_cfg = { + .current_lim = 1500, +}; +const INA226_Config fact_fe_12v_ps_cfg = { + .current_lim = 2000, +}; +const INA226_Config fact_fe_24v_ps_cfg = { + .current_lim = 1000, +}; +const INA226_Config fact_fe_28v_ps_cfg = { + .current_lim = 1000, +}; + +const INA226_Config fact_psu_12v_ps_cfg = { + .current_lim = 1000, +}; +const INA226_Config fact_gen_12v_ps_cfg = { + .current_lim = 1000, +}; +const INA226_Config fact_gen_24v_ps_cfg = { + .current_lim = 1000, +}; +const INA226_Config fact_bb_12v_ps_cfg = { + .current_lim = 1000, +}; +const INA226_Config fact_ec_12v_ps_cfg = { + .current_lim = 1000, +}; + +const INA226_Config fact_ec_3v_ps_cfg = { + .current_lim = 1000, +}; diff --git a/firmware/psu/platform/oc-sdr/schema/schema.c b/firmware/psu/platform/oc-sdr/schema/schema.c new file mode 100644 index 0000000000..8d01f83ab8 --- /dev/null +++ b/firmware/psu/platform/oc-sdr/schema/schema.c @@ -0,0 +1,369 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#include "common/inc/global/Framework.h" +#include "common/inc/ocmp_wrappers/ocmp_debugocgpio.h" +#include "common/inc/ocmp_wrappers/ocmp_debugi2c.h" +#include "common/inc/ocmp_wrappers/ocmp_eeprom_cat24c04.h" +#include "common/inc/ocmp_wrappers/ocmp_ina226.h" +#include "common/inc/ocmp_wrappers/ocmp_ltc4015.h" +#include "common/inc/ocmp_wrappers/ocmp_ltc4274.h" +#include "common/inc/ocmp_wrappers/ocmp_ltc4295.h" +#include "common/inc/ocmp_wrappers/ocmp_powersource.h" +#include "common/inc/ocmp_wrappers/ocmp_se98a.h" +#include "schema.h" + +/* Power SubSystem Configs */ +SCHEMA_IMPORT DriverStruct eeprom_psu_sid; +SCHEMA_IMPORT DriverStruct eeprom_psu_inv; + +SCHEMA_IMPORT const DriverStruct fact_bb_12v_ps_cfg; +SCHEMA_IMPORT const DriverStruct fact_bc_se98a; +SCHEMA_IMPORT const DriverStruct fact_fe_1v_ps_cfg; +SCHEMA_IMPORT const DriverStruct fact_fe_3v_ps_cfg; +SCHEMA_IMPORT const DriverStruct fact_fe_5v_ps_cfg; +SCHEMA_IMPORT const DriverStruct fact_fe_12v_ps_cfg; +SCHEMA_IMPORT const DriverStruct fact_fe_24v_ps_cfg; +SCHEMA_IMPORT const DriverStruct fact_fe_28v_ps_cfg; +SCHEMA_IMPORT const DriverStruct fact_gbc_12v_ps_cfg; +SCHEMA_IMPORT const DriverStruct fact_gen_12v_ps_cfg; +SCHEMA_IMPORT const DriverStruct fact_gen_24v_ps_cfg; +SCHEMA_IMPORT const DriverStruct fact_lithiumIon_cfg; +SCHEMA_IMPORT const DriverStruct fact_lion_12v_ps_cfg; +SCHEMA_IMPORT const DriverStruct fact_ltc4274_cfg; + +SCHEMA_IMPORT DriverStruct psu_bat_ps_12v; +SCHEMA_IMPORT DriverStruct psu_bb_ps_12v; +SCHEMA_IMPORT DriverStruct psu_fe_ps_1v; +SCHEMA_IMPORT DriverStruct psu_fe_ps_3v; +SCHEMA_IMPORT DriverStruct psu_fe_ps_5v; +SCHEMA_IMPORT DriverStruct psu_fe_ps_12v; +SCHEMA_IMPORT DriverStruct psu_fe_ps_24v; +SCHEMA_IMPORT DriverStruct psu_fe_ps_28v; +SCHEMA_IMPORT DriverStruct psu_gbc_ps_12v; +SCHEMA_IMPORT DriverStruct psu_gbc_ps_24v; +SCHEMA_IMPORT DriverStruct psu_gen_ps_12v; +SCHEMA_IMPORT DriverStruct psu_gen_ps_24v; +SCHEMA_IMPORT DriverStruct psu_lead_acid_ts; + +SCHEMA_IMPORT DriverStruct psu_pwr_int_bat_charger; +SCHEMA_IMPORT DriverStruct psu_pwr_pd; +SCHEMA_IMPORT DriverStruct psu_pwr_powerSource; +SCHEMA_IMPORT DriverStruct psu_pwr_pse; +SCHEMA_IMPORT DriverStruct psu_sensor_ts1; +SCHEMA_IMPORT DriverStruct psu_sensor_ts2; +SCHEMA_IMPORT DriverStruct psu_sensor_ts3; + +/*Debug SubSystem Configs*/ +SCHEMA_IMPORT DriverStruct debug_I2C0; +SCHEMA_IMPORT DriverStruct debug_I2C1; +SCHEMA_IMPORT DriverStruct debug_I2C2; +SCHEMA_IMPORT DriverStruct debug_I2C3; +SCHEMA_IMPORT DriverStruct debug_I2C4; +SCHEMA_IMPORT DriverStruct debug_I2C5; + +SCHEMA_IMPORT DriverStruct debug_psu_gpio_pa; +SCHEMA_IMPORT DriverStruct debug_psu_gpio_pb; +SCHEMA_IMPORT DriverStruct debug_psu_gpio_pc; +SCHEMA_IMPORT DriverStruct debug_psu_gpio_pd; +SCHEMA_IMPORT DriverStruct debug_psu_gpio_pe; +SCHEMA_IMPORT DriverStruct debug_psu_gpio_pf; +SCHEMA_IMPORT DriverStruct debug_psu_gpio_pg; +SCHEMA_IMPORT DriverStruct debug_psu_ioexpander; + +/* Functions */ +SCHEMA_IMPORT bool psuCore_pre_init(void *driver, void *returnValue); + + +const Component sys_schema[] = { + { + .name = "psuCore", + .ssHookSet = + &(SSHookSet){ + .preInitFxn = (ssHook_Cb)psuCore_pre_init, + }, + .driver_cfg = &psu_pwr_powerSource, + .components = (Component[]){ + { + .name = "comp_all", + .driver = &PWRSRC, + .driver_cfg = &psu_pwr_powerSource, + }, + { + .name = "eeprom1", + .driver = &CAT24C04_psu_inv, + .driver_cfg = &eeprom_psu_sid, + }, + { + .name = "eeprom2", + .driver = &CAT24C04_psu_inv, + .driver_cfg = &eeprom_psu_inv, + }, + { + .name = "lion", + .components = (Component[]){ + { + .name = "battery", + .driver = <C4015, + .driver_cfg = &psu_pwr_int_bat_charger, + .factory_config = &fact_lithiumIon_cfg, + }, + {} + } + }, + { + .name = "pse", + .driver = <C4274, + .driver_cfg = &psu_pwr_pse, + .factory_config = &fact_ltc4274_cfg, + }, + { + .name = "pd", + .driver = <C4295, + .driver_cfg = &psu_pwr_pd, + }, + { + .name = "sensors", + .components = (Component[]){ + { + .name = "temp_sensor1", + .driver = &SE98A, + .driver_cfg = &psu_sensor_ts1, + .factory_config = &fact_bc_se98a, + }, + { + .name = "temp_sensor2", + .driver = &SE98A, + .driver_cfg = &psu_sensor_ts2, + .factory_config = &fact_bc_se98a, + }, + { + .name = "temp_sensor3", + .driver = &SE98A, + .driver_cfg = &psu_sensor_ts3, + .factory_config = &fact_bc_se98a, + }, + {} + }, + }, + {} + }, + }, + { + .name = "psubms", + .components = (Component[]){ + { + .name = "comp_all", + .postDisabled = POST_DISABLED, + }, + { + .name = "debugI2C", + .postDisabled = POST_DISABLED, + .components = (Component[]){ + { + .name = "comp_all", + .postDisabled = POST_DISABLED + }, + { + .name = "bus0", + .driver = &OC_I2C, + .driver_cfg = &debug_I2C0, + .postDisabled = POST_DISABLED + }, + { + .name = "bus1", + .driver = &OC_I2C, + .driver_cfg = &debug_I2C1, + .postDisabled = POST_DISABLED + }, + { + .name = "bus2", + .driver = &OC_I2C, + .driver_cfg = &debug_I2C2, + .postDisabled = POST_DISABLED + }, + { + .name = "bus3", + .driver = &OC_I2C, + .driver_cfg = &debug_I2C3, + .postDisabled = POST_DISABLED + }, + { + .name = "bus4", + .driver = &OC_I2C, + .driver_cfg = &debug_I2C4, + .postDisabled = POST_DISABLED + }, + { + .name = "bus5", + .driver = &OC_I2C, + .driver_cfg = &debug_I2C5, + .postDisabled = POST_DISABLED + }, + {} + }, + }, + { + .name = "debugGPIO", + .postDisabled = POST_DISABLED, + .components = (Component[]){ + { + .name = "comp_all", + .postDisabled = POST_DISABLED, + }, + { + .name = "PA", + .driver = &OC_GPIO, + .driver_cfg = &debug_psu_gpio_pa, + .postDisabled = POST_DISABLED, + }, + { + .name = "PB", + .driver = &OC_GPIO, + .driver_cfg =&debug_psu_gpio_pb, + .postDisabled = POST_DISABLED, + }, + { + .name = "PC", + .driver = &OC_GPIO, + .driver_cfg = &debug_psu_gpio_pc, + .postDisabled = POST_DISABLED, + }, + { + .name = "PD", + .driver = &OC_GPIO, + .driver_cfg = &debug_psu_gpio_pd, + .postDisabled = POST_DISABLED, + }, + { + .name = "PE", + .driver = &OC_GPIO, + .driver_cfg = &debug_psu_gpio_pe, + .postDisabled = POST_DISABLED, + }, + { + .name = "PF", + .driver = &OC_GPIO, + .driver_cfg = &debug_psu_gpio_pf, + .postDisabled = POST_DISABLED, + }, + { + .name = "PG", + .driver = &OC_GPIO, + .driver_cfg = &debug_psu_gpio_pg, + .postDisabled = POST_DISABLED, + }, + {} + }, + }, + { + .name = "debugIOexpander", + .postDisabled = POST_DISABLED, + .components = (Component[]){ + { + .name = "comp_all", + .postDisabled = POST_DISABLED, + }, + { + .name = "ioexpander", + .driver = &OC_GPIO, + .driver_cfg = &debug_psu_ioexpander, + }, + {} + }, + }, + { + .name = "fe1", + .components = (Component[]){ + { + .name = "current_sensor1", + .driver = &INA226, + .driver_cfg = &psu_fe_ps_1v, + .factory_config = &fact_fe_1v_ps_cfg, + }, + { + .name = "current_sensor2", + .driver = &INA226, + .driver_cfg = &psu_fe_ps_3v, + .factory_config = &fact_fe_3v_ps_cfg, + }, + { + .name = "current_sensor3", + .driver = &INA226, + .driver_cfg = &psu_fe_ps_5v, + .factory_config = &fact_fe_5v_ps_cfg, + }, + {} + } + }, + { + .name = "fe2", + .components = (Component[]){ + { + .name = "current_sensor4", + .driver = &INA226, + .driver_cfg = &psu_fe_ps_12v, + .factory_config = &fact_fe_12v_ps_cfg, + }, + { + .name = "current_sensor5", + .driver = &INA226, + .driver_cfg = &psu_fe_ps_24v, + .factory_config = &fact_fe_24v_ps_cfg, + }, + { + .name = "current_sensor6", + .driver = &INA226, + .driver_cfg = &psu_fe_ps_28v, + .factory_config = &fact_fe_28v_ps_cfg, + }, + {} + }, + }, + { + .name = "gbc", + .components = (Component[]){ + { + .name = "current_sensor1", + .driver = &INA226, + .driver_cfg = &psu_gbc_ps_12v, + .factory_config = &fact_gbc_12v_ps_cfg, + }, + { + .name = "current_sensor2", + .driver = &INA226, + .driver_cfg = &psu_gen_ps_12v, + .factory_config = &fact_gen_12v_ps_cfg, + }, + { + .name = "current_sensor3", + .driver = &INA226, + .driver_cfg = &psu_gen_ps_24v, + .factory_config = &fact_gen_24v_ps_cfg, + }, + {} + }, + }, + { + .name = "bb", + .components = (Component[]){ + { + .name = "current_sensor", + .driver = &INA226, + .driver_cfg = &psu_bb_ps_12v, + .factory_config = &fact_bb_12v_ps_cfg, + }, + {} + }, + }, + {} + }, + }, + {} +}; diff --git a/firmware/psu/platform/oc-sdr/schema/schema.h b/firmware/psu/platform/oc-sdr/schema/schema.h new file mode 100644 index 0000000000..e5f11ff9d5 --- /dev/null +++ b/firmware/psu/platform/oc-sdr/schema/schema.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef _SCHEMA_H +#define _SCHEMA_H + +#define SUBSYSTEM_COUNT OC_SS_MAX_LIMIT + +#endif /* _SCHEMA_H */ diff --git a/firmware/psu/psu.cfg b/firmware/psu/psu.cfg new file mode 100644 index 0000000000..7a8bbb63be --- /dev/null +++ b/firmware/psu/psu.cfg @@ -0,0 +1,557 @@ +/* + * Copyright (c) 2015-2016, Texas Instruments Incorporated + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Texas Instruments Incorporated nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + + +/* ================ Clock configuration ================ */ +var Clock = xdc.useModule('ti.sysbios.knl.Clock'); +/* + * Default value is family dependent. For example, Linux systems often only + * support a minimum period of 10000 us and multiples of 10000 us. + * TI platforms have a default of 1000 us. + */ +Clock.tickPeriod = 1000; + + + +/* ================ Defaults (module) configuration ================ */ +var Defaults = xdc.useModule('xdc.runtime.Defaults'); +/* + * A flag to allow module names to be loaded on the target. Module name + * strings are placed in the .const section for debugging purposes. + * + * Pick one: + * - true (default) + * Setting this parameter to true will include name strings in the .const + * section so that Errors and Asserts are easier to debug. + * - false + * Setting this parameter to false will reduce footprint in the .const + * section. As a result, Error and Assert messages will contain an + * "unknown module" prefix instead of the actual module name. + */ +Defaults.common$.namedModule = true; +//Defaults.common$.namedModule = false; + + + +/* ================ Error configuration ================ */ +var Error = xdc.useModule('xdc.runtime.Error'); +/* + * This function is called to handle all raised errors, but unlike + * Error.raiseHook, this function is responsible for completely handling the + * error with an appropriately initialized Error_Block. + * + * Pick one: + * - Error.policyDefault (default) + * Calls Error.raiseHook with an initialized Error_Block structure and logs + * the error using the module's logger. + * - Error.policySpin + * Simple alternative that traps on a while(1) loop for minimized target + * footprint. + * Using Error.policySpin, the Error.raiseHook will NOT called. + */ +Error.policyFxn = Error.policyDefault; +//Error.policyFxn = Error.policySpin; + +/* + * If Error.policyFxn is set to Error.policyDefault, this function is called + * whenever an error is raised by the Error module. + * + * Pick one: + * - Error.print (default) + * Errors are formatted and output via System_printf() for easier + * debugging. + * - null + * Errors are not formatted or logged. This option reduces code footprint. + * - non-null function + * Errors invoke custom user function. See the Error module documentation + * for more details. + */ +Error.raiseHook = Error.print; +//Error.raiseHook = null; +//Error.raiseHook = "&myErrorFxn"; + +/* + * If Error.policyFxn is set to Error.policyDefault, this option applies to the + * maximum number of times the Error.raiseHook function can be recursively + * invoked. This option limits the possibility of an infinite recursion that + * could lead to a stack overflow. + * The default value is 16. + */ +Error.maxDepth = 2; + + + +/* ================ Hwi configuration ================ */ +var halHwi = xdc.useModule('ti.sysbios.hal.Hwi'); +var m3Hwi = xdc.useModule('ti.sysbios.family.arm.m3.Hwi'); +/* + * Checks for Hwi (system) stack overruns while in the Idle loop. + * + * Pick one: + * - true (default) + * Checks the top word for system stack overflows during the idle loop and + * raises an Error if one is detected. + * - false + * Disabling the runtime check improves runtime performance and yields a + * reduced flash footprint. + */ +halHwi.checkStackFlag = true; +//halHwi.checkStackFlag = false; + +/* + * The following options alter the system's behavior when a hardware exception + * is detected. + * + * Pick one: + * - Hwi.enableException = true + * This option causes the default m3Hwi.excHandlerFunc function to fully + * decode an exception and dump the registers to the system console. + * This option raises errors in the Error module and displays the + * exception in ROV. + * - Hwi.enableException = false + * This option reduces code footprint by not decoding or printing the + * exception to the system console. + * It however still raises errors in the Error module and displays the + * exception in ROV. + * - Hwi.excHandlerFunc = null + * This is the most aggressive option for code footprint savings; but it + * can difficult to debug exceptions. It reduces flash footprint by + * plugging in a default while(1) trap when exception occur. This option + * does not raise an error with the Error module. + */ +m3Hwi.enableException = true; +//m3Hwi.enableException = false; +//m3Hwi.excHandlerFunc = null; + +/* + * Enable hardware exception generation when dividing by zero. + * + * Pick one: + * - 0 (default) + * Disables hardware exceptions when dividing by zero + * - 1 + * Enables hardware exceptions when dividing by zero + */ +m3Hwi.nvicCCR.DIV_0_TRP = 0; +//m3Hwi.nvicCCR.DIV_0_TRP = 1; + +/* + * Enable hardware exception generation for invalid data alignment. + * + * Pick one: + * - 0 (default) + * Disables hardware exceptions for data alignment + * - 1 + * Enables hardware exceptions for data alignment + */ +m3Hwi.nvicCCR.UNALIGN_TRP = 0; +//m3Hwi.nvicCCR.UNALIGN_TRP = 1; + + + +/* ================ Idle configuration ================ */ +var Idle = xdc.useModule('ti.sysbios.knl.Idle'); +/* + * The Idle module is used to specify a list of functions to be called when no + * other tasks are running in the system. + * + * Functions added here will be run continuously within the idle task. + * + * Function signature: + * Void func(Void); + */ +//Idle.addFunc("&myIdleFunc"); + + + +/* ================ Kernel (SYS/BIOS) configuration ================ */ +var BIOS = xdc.useModule('ti.sysbios.BIOS'); +/* + * Enable asserts in the BIOS library. + * + * Pick one: + * - true (default) + * Enables asserts for debugging purposes. + * - false + * Disables asserts for a reduced code footprint and better performance. + */ +//BIOS.assertsEnabled = true; +BIOS.assertsEnabled = false; + +/* + * Specify default heap size for BIOS. + */ +//BIOS.heapSize = 6144; +BIOS.heapSize = 8192; +//BIOS.heapSize = 9216; +//BIOS.heapSize = 102400 +/* + * A flag to determine if xdc.runtime sources are to be included in a custom + * built BIOS library. + * + * Pick one: + * - false (default) + * The pre-built xdc.runtime library is provided by the respective target + * used to build the application. + * - true + * xdc.runtime library souces are to be included in the custom BIOS + * library. This option yields the most efficient library in both code + * footprint and runtime performance. + */ +BIOS.includeXdcRuntime = false; +//BIOS.includeXdcRuntime = true; + +/* + * The SYS/BIOS runtime is provided in the form of a library that is linked + * with the application. Several forms of this library are provided with the + * SYS/BIOS product. + * + * Pick one: + * - BIOS.LibType_Custom + * Custom built library that is highly optimized for code footprint and + * runtime performance. + * - BIOS.LibType_Debug + * Custom built library that is non-optimized that can be used to + * single-step through APIs with a debugger. + * + */ +BIOS.libType = BIOS.LibType_Custom; +//BIOS.libType = BIOS.LibType_Debug; + +/* + * Runtime instance creation enable flag. + * + * Pick one: + * - true (default) + * Allows Mod_create() and Mod_delete() to be called at runtime which + * requires a default heap for dynamic memory allocation. + * - false + * Reduces code footprint by disallowing Mod_create() and Mod_delete() to + * be called at runtime. Object instances are constructed via + * Mod_construct() and destructed via Mod_destruct(). + */ +BIOS.runtimeCreatesEnabled = true; +//BIOS.runtimeCreatesEnabled = false; + +/* + * Enable logs in the BIOS library. + * + * Pick one: + * - true (default) + * Enables logs for debugging purposes. + * - false + * Disables logging for reduced code footprint and improved runtime + * performance. + */ +//BIOS.logsEnabled = true; +BIOS.logsEnabled = false; + + + +/* ================ Memory configuration ================ */ +var Memory = xdc.useModule('xdc.runtime.Memory'); +/* + * The Memory module itself simply provides a common interface for any + * variety of system and application specific memory management policies + * implemented by the IHeap modules(Ex. HeapMem, HeapBuf). + */ + + + +/* ================ Program configuration ================ */ +/* + * Program.stack is ignored with IAR. Use the project options in + * IAR Embedded Workbench to alter the system stack size. + */ +if (!Program.build.target.$name.match(/iar/)) { + /* + * Reducing the system stack size (used by ISRs and Swis) to reduce + * RAM usage. + */ + Program.stack = 768; +} + + + +/* + * Enable Semihosting for GNU targets to print to CCS console + */ +if (Program.build.target.$name.match(/gnu/)) { + var SemiHost = xdc.useModule('ti.sysbios.rts.gnu.SemiHostSupport'); +} +/* ================ Semaphore configuration ================ */ +var Semaphore = xdc.useModule('ti.sysbios.knl.Semaphore'); +/* + * Enables global support for Task priority pend queuing. + * + * Pick one: + * - true (default) + * This allows pending tasks to be serviced based on their task priority. + * - false + * Pending tasks are services based on first in, first out basis. + * + * When using BIOS in ROM: + * This option must be set to false. + */ +//Semaphore.supportsPriority = true; +Semaphore.supportsPriority = false; + +/* + * Allows for the implicit posting of events through the semaphore, + * disable for additional code saving. + * + * Pick one: + * - true + * This allows the Semaphore module to post semaphores and events + * simultaneously. + * - false (default) + * Events must be explicitly posted to unblock tasks. + * + */ +//Semaphore.supportsEvents = true; +Semaphore.supportsEvents = false; + + + +/* ================ Swi configuration ================ */ +var Swi = xdc.useModule('ti.sysbios.knl.Swi'); +/* + * A software interrupt is an object that encapsulates a function to be + * executed and a priority. Software interrupts are prioritized, preempt tasks + * and are preempted by hardware interrupt service routines. + * + * This module is included to allow Swi's in a users' application. + */ + + + +/* ================ System configuration ================ */ +var System = xdc.useModule('xdc.runtime.System'); +/* + * The Abort handler is called when the system exits abnormally. + * + * Pick one: + * - System.abortStd (default) + * Call the ANSI C Standard 'abort()' to terminate the application. + * - System.abortSpin + * A lightweight abort function that loops indefinitely in a while(1) trap + * function. + * - A custom abort handler + * A user-defined function. See the System module documentation for + * details. + */ +System.abortFxn = System.abortStd; +//System.abortFxn = System.abortSpin; +//System.abortFxn = "&myAbortSystem"; + +/* + * The Exit handler is called when the system exits normally. + * + * Pick one: + * - System.exitStd (default) + * Call the ANSI C Standard 'exit()' to terminate the application. + * - System.exitSpin + * A lightweight exit function that loops indefinitely in a while(1) trap + * function. + * - A custom exit function + * A user-defined function. See the System module documentation for + * details. + */ +System.exitFxn = System.exitStd; +//System.exitFxn = System.exitSpin; +//System.exitFxn = "&myExitSystem"; + +/* + * Minimize exit handler array in the System module. The System module includes + * an array of functions that are registered with System_atexit() which is + * called by System_exit(). The default value is 8. + */ +System.maxAtexitHandlers = 2; + +/* + * The System.SupportProxy defines a low-level implementation of System + * functions such as System_printf(), System_flush(), etc. + * + * Pick one pair: + * - SysMin + * This module maintains an internal configurable circular buffer that + * stores the output until System_flush() is called. + * The size of the circular buffer is set via SysMin.bufSize. + * - SysCallback + * SysCallback allows for user-defined implementations for System APIs. + * The SysCallback support proxy has a smaller code footprint and can be + * used to supply custom System_printf services. + * The default SysCallback functions point to stub functions. See the + * SysCallback module's documentation. + */ +var SysMin = xdc.useModule('xdc.runtime.SysMin'); +SysMin.bufSize = 128; +System.SupportProxy = SysMin; +//var SysCallback = xdc.useModule('xdc.runtime.SysCallback'); +//System.SupportProxy = SysCallback; +//SysCallback.abortFxn = "&myUserAbort"; +//SysCallback.exitFxn = "&myUserExit"; +//SysCallback.flushFxn = "&myUserFlush"; +//SysCallback.putchFxn = "&myUserPutch"; +//SysCallback.readyFxn = "&myUserReady"; + + + + +/* ================ Task configuration ================ */ +var Task = xdc.useModule('ti.sysbios.knl.Task'); +/* + * Check task stacks for overflow conditions. + * + * Pick one: + * - true (default) + * Enables runtime checks for task stack overflow conditions during + * context switching ("from" and "to") + * - false + * Disables runtime checks for task stack overflow conditions. + */ +Task.checkStackFlag = true; +//Task.checkStackFlag = false; + +/* + * Set the default task stack size when creating tasks. + * + * The default is dependent on the device being used. Reducing the default stack + * size yields greater memory savings. + */ +Task.defaultStackSize = 512; + +/* + * Enables the idle task. + * + * Pick one: + * - true (default) + * Creates a task with priority of 0 which calls idle hook functions. This + * option must be set to true to gain power savings provided by the Power + * module. + * - false + * No idle task is created. This option consumes less memory as no + * additional default task stack is needed. + * To gain power savings by the Power module without having the idle task, + * add Idle.run as the Task.allBlockedFunc. + */ +Task.enableIdleTask = true; +//Task.enableIdleTask = false; +//Task.allBlockedFunc = Idle.run; + +/* + * If Task.enableIdleTask is set to true, this option sets the idle task's + * stack size. + * + * Reducing the idle stack size yields greater memory savings. + */ +Task.idleTaskStackSize = 512; + +/* + * Reduce the number of task priorities. + * The default is 16. + * Decreasing the number of task priorities yield memory savings. + */ +Task.numPriorities = 16; + + + +/* ================ Text configuration ================ */ +var Text = xdc.useModule('xdc.runtime.Text'); +/* + * These strings are placed in the .const section. Setting this parameter to + * false will save space in the .const section. Error, Assert and Log messages + * will print raw ids and args instead of a formatted message. + * + * Pick one: + * - true (default) + * This option loads test string into the .const for easier debugging. + * - false + * This option reduces the .const footprint. + */ +Text.isLoaded = true; +//Text.isLoaded = false; + + + +/* ================ Types configuration ================ */ +var Types = xdc.useModule('xdc.runtime.Types'); +/* + * This module defines basic constants and types used throughout the + * xdc.runtime package. + */ + + + +/* ================ TI-RTOS middleware configuration ================ */ +var mwConfig = xdc.useModule('ti.mw.Config'); +/* + * Include TI-RTOS middleware libraries + */ + + + +/* ================ TI-RTOS drivers' configuration ================ */ +var driversConfig = xdc.useModule('ti.drivers.Config'); +/* + * Include TI-RTOS drivers + * + * Pick one: + * - driversConfig.LibType_NonInstrumented (default) + * Use TI-RTOS drivers library optimized for footprint and performance + * without asserts or logs. + * - driversConfig.LibType_Instrumented + * Use TI-RTOS drivers library for debugging with asserts and logs enabled. + */ +driversConfig.libType = driversConfig.LibType_NonInstrumented; +//driversConfig.libType = driversConfig.LibType_Instrumented; + + +var Main = xdc.useModule('xdc.runtime.Main'); + +/*var m3Hwi0Params = new m3Hwi.Params(); +m3Hwi0Params.instance.name = "m3Hwi0"; +m3Hwi0Params.enableInt = false; +Program.global.m3Hwi0 = m3Hwi.create(76, "&UART4IntHandler", m3Hwi0Params); + +/* +var m3Hwi1Params = new m3Hwi.Params(); +m3Hwi1Params.instance.name = "m3Hwi1"; +Program.global.m3Hwi1 = m3Hwi.create(60, "&uDMAIntHandler", m3Hwi1Params); +*/ + +/*var m3Hwi2Params = new m3Hwi.Params(); +m3Hwi2Params.instance.name = "m3Hwi2"; +m3Hwi2Params.enableInt = false; +Program.global.m3Hwi2 = m3Hwi.create(61, "&uDMAErrorHandler", m3Hwi2Params); +/* ================ Application Specific Instances ================ */ \ No newline at end of file diff --git a/firmware/psu/psuConfiguration.ccxml b/firmware/psu/psuConfiguration.ccxml new file mode 100644 index 0000000000..6eb50adf7b --- /dev/null +++ b/firmware/psu/psuConfiguration.ccxml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/firmware/psu/src/Board.h b/firmware/psu/src/Board.h new file mode 100644 index 0000000000..7a8842ee49 --- /dev/null +++ b/firmware/psu/src/Board.h @@ -0,0 +1,92 @@ +/******************************************************************************* + Filename: Board.h + Revised: $Date: 2015-06-02 11:18:40 -0700 (Tue, 02 Jun 2015) $ + Revision: $Revision: 43957 $ + + Description: This file contains utility functions. + + Copyright 2014 Texas Instruments Incorporated. All rights reserved. + + IMPORTANT: Your use of this Software is limited to those specific rights + granted under the terms of a software license agreement between the user + who downloaded the software, his/her employer (which must be your employer) + and Texas Instruments Incorporated (the "License"). You may not use this + Software unless you agree to abide by the terms of the License. The License + limits your use, and you acknowledge, that the Software may not be modified, + copied or distributed unless embedded on a Texas Instruments microcontroller + or used solely and exclusively in conjunction with a Texas Instruments radio + frequency transceiver, which is integrated into your product. Other than for + the foregoing purpose, you may not use, reproduce, copy, prepare derivative + works of, modify, distribute, perform, display or sell this Software and/or + its documentation for any purpose. + + YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE + PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, + INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE, + NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL + TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT, + NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER + LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES + INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE + OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT + OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES + (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS. + + Should you have any questions regarding your right to use this Software, + contact Texas Instruments Incorporated at www.TI.com. +*******************************************************************************/ +#ifndef __BOARD_H +#define __BOARD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "common/inc/global/OC_CONNECT1.h" + +#define Board_initEMAC OC_CONNECT1_initEMAC +#define Board_initGeneral OC_CONNECT1_initGeneral +#define Board_initGPIO OC_CONNECT1_initGPIO +#define Board_initI2C OC_CONNECT1_initI2C +#define Board_initUART OC_CONNECT1_initUART +#define Board_initUSB OC_CONNECT1_initUSB +#define Board_initWatchdog OC_CONNECT1_initWatchdog + +#define Board_IOEXP_ALERT OC_EC_GBC_IOEXP71_ALERT +#define Board_ECINA_ALERT OC_EC_GBC_INA_ALERT +#define Board_APINA_ALERT OC_EC_GBC_AP_INA_ALERT +#define Board_SDRFPGA_TEMPINA_ALERT OC_EC_SDR_FPGA_TEMP_INA_ALERT +#define Board_SDR_INA_ALERT OC_EC_SDR_INA_ALERT +#define Board_RFFE_TEMP_INA_ALERT OC_EC_RFFE_TEMP_INA_ALERT +#define Board_SYNC_IOEXP_ALERT OC_EC_SYNC_IOEXP_ALERT +#define Board_LeadAcidAlert OC_EC_PWR_LACID_ALERT +#define Board_LithiumIonAlert OC_EC_PWR_LION_ALERT +#define Board_PSEALERT OC_EC_GBC_PSE_ALERT +#define Board_PD_PWRGDAlert OC_EC_PD_PWRGD_ALERT +#define Board_SOC_UART3_TX OC_EC_SOC_UART3_TX + +#define Board_I2C0 OC_CONNECT1_I2C0 +#define Board_I2C1 OC_CONNECT1_I2C1 +#define Board_I2C2 OC_CONNECT1_I2C2 +#define Board_I2C3 OC_CONNECT1_I2C3 +#define Board_I2C4 OC_CONNECT1_I2C4 +#define Board_I2C6 OC_CONNECT1_I2C6 +#define Board_I2C7 OC_CONNECT1_I2C7 +#define Board_I2C8 OC_CONNECT1_I2C8 +#define Board_I2CCOUNT OC_CONNECT1_I2CCOUNT + +#define Board_USBHOST OC_CONNECT1_USBHOST +#define Board_USBDEVICE OC_CONNECT1_USBDEVICE + +// TODO: maybe rename to "UART_GSM" and stuff to be more abstracted from HW +#define Board_UART0 OC_CONNECT1_UART0 +#define Board_UART3 OC_CONNECT1_UART3 +#define Board_UART4 OC_CONNECT1_UART4 +#define Board_UARTXR0 OC_CONNECT1_UARTXR0 +#define Board_WATCHDOG0 OC_CONNECT1_WATCHDOG0 + +#ifdef __cplusplus +} +#endif + +#endif /* __BOARD_H */ diff --git a/firmware/psu/src/bigbrother.c b/firmware/psu/src/bigbrother.c new file mode 100644 index 0000000000..f073dbafaf --- /dev/null +++ b/firmware/psu/src/bigbrother.c @@ -0,0 +1,369 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** +#include "inc/common/bigbrother.h" + +#include "Board.h" +#include "comm/gossiper.h" +#include "common/inc/global/ocmp_frame.h" +#include "drivers/OcGpio.h" +#include "inc/common/post.h" +#include "inc/common/system_states.h" +#include "inc/subsystem/hci/hci_buzzer.h" +#include "inc/utils/ocmp_util.h" +#include "registry/SSRegistry.h" + +#include + +#include +extern OcGpio_Port pwr_io; +extern OcGpio_Port ec_io; + +OcGpio_Pin pin_24v = { &pwr_io, 3}; +OcGpio_Pin pin_5v0 = { &pwr_io, 4}; +OcGpio_Pin pin_3v3 = { &pwr_io, 5}; +OcGpio_Pin pin_gbcv2_on = { &pwr_io, 6}; +OcGpio_Pin pin_12v_bb = { &pwr_io, 7}; +OcGpio_Pin pin_12v_fe = { &pwr_io, 8}; +OcGpio_Pin pin_20v_fe = { &pwr_io, 9}; +OcGpio_Pin pin_1v8 = { &pwr_io, 10}; + + +/* Global Task Configuration Variables */ +Task_Struct bigBrotherTask; +Char bigBrotherTaskStack[BIGBROTHER_TASK_STACK_SIZE]; + +eSubSystemStates oc_sys_state = SS_STATE_PWRON; +//***************************************************************************** +// HANDLES DEFINITION +//***************************************************************************** +/* Queue object */ +/* + * Semaphore for the Big Brother task where it will be waiting on. + * Sub systems or Gossiper post this with respective queues filled in. + */ +Semaphore_Handle semBigBrotherMsg; + +static Queue_Struct bigBrotherRxMsg; +static Queue_Struct bigBrotherTxMsg; + +/* + * bigBrotherRxMsgQueue - Used by the gossiper to pass the frame once recived + * from Ethernet or UART and processed and needs to forward to bigBrother. + */ +Queue_Handle bigBrotherRxMsgQueue; +/* + * bigBrotherTxMsgQueue - Used by the BigBrother to pass the frame once + * underlying subsystem processed it (GPP/RF etc) and need to send back + * to Gosipper. This is the one all the subsystem will be listening only. + */ +Queue_Handle bigBrotherTxMsgQueue; + +/***************************************************************************** + * FUNCTION DECLARATIONS + *****************************************************************************/ +static void bigbrother_taskfxn(UArg a0, UArg a1); +static void bigbrother_init(void); +static ReturnStatus bigbrother_process_rx_msg(uint8_t *pMsg); +static ReturnStatus bigbrother_process_tx_msg(uint8_t *pMsg); +extern void post_createtask(void); +static void bigborther_initiate_post(void); + +extern void gossiper_createtask(void); +extern void usb_rx_createtask(void); +extern void usb_tx_createtask(void); +extern void uartdma_rx_createtask(void); +extern void uartdma_tx_createtask(void); +//extern void watchdog_create_task(void);; + +/***************************************************************************** + ** FUNCTION NAME : bb_sys_post_complete + ** + ** DESCRIPTION : Get POST results from EEPROM. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : ReturnStatus + ** + *****************************************************************************/ +ReturnStatus bb_sys_post_complete() +{ + ReturnStatus status = RETURN_OK; + LOGGER_DEBUG("BIGBROTHER:INFO::POST test is completed.\n"); + static uint8_t count = 0; + + if (count == 0) { + // Task_sleep(60000); + // TODO: Starting UART DMA Interface based on EBMP. + uartdma_rx_createtask(); // P - 07 + uartdma_tx_createtask(); // P - 07 + count++; + // uart_enable(); + /* TODO: enable this back */ +#if ENABLE_POWER + OcGpio_configure(&pin_24v, + OCGPIO_CFG_OUTPUT | OCGPIO_CFG_OUT_LOW); + OcGpio_write(&pin_24v, 1); + + OcGpio_configure(&pin_5v0, + OCGPIO_CFG_OUTPUT | OCGPIO_CFG_OUT_LOW); + OcGpio_write(&pin_5v0, 1); + + OcGpio_configure(&pin_3v3, + OCGPIO_CFG_OUTPUT | OCGPIO_CFG_OUT_LOW); + OcGpio_write(&pin_3v3, 1); + + OcGpio_configure(&pin_gbcv2_on, + OCGPIO_CFG_OUTPUT | OCGPIO_CFG_OUT_LOW); + OcGpio_write(&pin_gbcv2_on, 1); + + OcGpio_configure(&pin_12v_bb, + OCGPIO_CFG_OUTPUT | OCGPIO_CFG_OUT_LOW); + OcGpio_write(&pin_12v_bb, 1); + + OcGpio_configure(&pin_12v_fe, + OCGPIO_CFG_OUTPUT | OCGPIO_CFG_OUT_LOW); + OcGpio_write(&pin_12v_fe, 1); + + OcGpio_configure(&pin_20v_fe, + OCGPIO_CFG_OUTPUT | OCGPIO_CFG_OUT_LOW); + OcGpio_write(&pin_20v_fe, 1); + + OcGpio_configure(&pin_1v8, + OCGPIO_CFG_OUTPUT | OCGPIO_CFG_OUT_LOW); + OcGpio_write(&pin_1v8, 1); +#endif + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : bigbrother_process_tx_msg + ** + ** DESCRIPTION : Processes the big brother outgoing messages. + ** + ** ARGUMENTS : Pointer to BIGBROTHER_TXEvt_t structure + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static ReturnStatus bigbrother_process_tx_msg(uint8_t *pMsg) +{ + ReturnStatus status = RETURN_OK; + LOGGER_DEBUG("BIGBROTHER:INFO:: Processing Big Brother TX Message.\n"); + if (pMsg != NULL) { + Util_enqueueMsg(gossiperTxMsgQueue, semGossiperMsg, (uint8_t*) pMsg); + } else { + LOGGER_ERROR("BIGBROTHER::ERROR::No Valid Pointer.\n"); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : bigbrother_process_rx_msg + ** + ** DESCRIPTION : Processes the big brother incoming messages. + ** + ** ARGUMENTS : Pointer to BIGBROTHER_RXEvt_t structure + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static ReturnStatus bigbrother_process_rx_msg(uint8_t *pMsg) +{ + ReturnStatus status = RETURN_OK; + LOGGER_DEBUG("BIGBROTHER:INFO:: Processing Big Brother RX Message.\n"); + OCMPMessageFrame * pOCMPMessageFrame = (OCMPMessageFrame *) pMsg; + if (pOCMPMessageFrame != NULL) { + LOGGER_DEBUG("BIGBROTHER:INFO:: RX Msg recieved with Length: 0x%x," + "Interface: 0x%x, Seq.No: 0x%x, TimeStamp: 0x%x.\n", + pOCMPMessageFrame->header.ocmpFrameLen, + pOCMPMessageFrame->header.ocmpInterface, + pOCMPMessageFrame->header.ocmpSeqNumber, + pOCMPMessageFrame->header.ocmpTimestamp); + // Forward this to respective subsystem. + if (!SSRegistry_sendMessage(pOCMPMessageFrame->message.subsystem, + pMsg)) { + LOGGER_ERROR("BIGBROTHER::ERROR::Subsystem %d doesn't exist\n", + pOCMPMessageFrame->message.subsystem); + free(pMsg); + } + } else { + LOGGER_ERROR("BIGBROTHER:ERROR:: No message recieved.\n"); + free(pMsg); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : bigborther_initiate_post + ** + ** DESCRIPTION : Creates POST test task. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void bigborther_initiate_post(void) +{ + LOGGER_DEBUG("BIGBROTHER:INFO::Creating task to perform POST.\n"); + post_createtask(); +} + +/***************************************************************************** + ** FUNCTION NAME : bigbrother_ioexp_init + ** + ** DESCRIPTION : Initializes Io expander SX1509. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +OcGpio_Pin pin_v5_a_pgood = { &ec_io, OC_EC_PGOOD_5V0}; +OcGpio_Pin pin_v12_a_pgood = { &ec_io, OC_EC_PGOOD_12V0}; + +ReturnStatus bigbrother_ioexp_init(void) +{ + ReturnStatus status = RETURN_OK; + + OcGpio_init(&pwr_io); + + /* Initialize pins that aren't covered yet by a subsystem */ + OcGpio_configure(&pin_v5_a_pgood, OCGPIO_CFG_INPUT); + OcGpio_configure(&pin_v12_a_pgood, OCGPIO_CFG_INPUT); + + return status; +} + +/******************************************************************************* + ** FUNCTION NAME : bigborther_spwan_task + ** + ** DESCRIPTION : Application task start up point for open cellular. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + ******************************************************************************/ +static void bigborther_spwan_task(void) +{ + /* Read OC UID EEPROM */ + + /* Check the list for possible devices connected. */ + + /* Launches other tasks */ +// usb_rx_createtask(); // P - 05 +// usb_tx_createtask(); // P - 04 + gossiper_createtask(); // P - 06 +// ebmp_create_task(); +// watchdog_create_task(); + + /* Initialize subsystem interface to set up interfaces and launch + * subsystem tasks */ + SSRegistry_init(); + +} + +/***************************************************************************** + ** FUNCTION NAME : bigbrother_init + ** + ** DESCRIPTION : Initializes the Big Brother task. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void bigbrother_init(void) +{ + /*Creating Semaphore for RX Message Queue*/ + semBigBrotherMsg = Semaphore_create(0, NULL, NULL); + if (semBigBrotherMsg == NULL) { + LOGGER_ERROR("BIGBROTHER:ERROR::BIGBROTHER RX Semaphore creation failed.\n"); + } + /*Creating RX Message Queue*/ + bigBrotherRxMsgQueue = Util_constructQueue(&bigBrotherRxMsg); + LOGGER_DEBUG("BIGBROTHER:INFO::Constructing message Queue for 0x%x Big Brother RX Messages.\n", + bigBrotherRxMsgQueue); + + /*Creating TX Message Queue*/ + bigBrotherTxMsgQueue = Util_constructQueue(&bigBrotherTxMsg); + LOGGER_DEBUG("BIGBROTHER:INFO::Constructing message Queue for 0x%x Big Brother RX Messages.\n", + bigBrotherTxMsgQueue); +} + +/***************************************************************************** + ** FUNCTION NAME : bigbrother_taskfxn + ** + ** DESCRIPTION : handles the system state and subsystem states. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void bigbrother_taskfxn(UArg a0, UArg a1) +{ + bigbrother_init(); + + /* Initialize GPIO Expander SX1509 */ + bigbrother_ioexp_init(); +// hci_buzzer_beep(1); + + //Create Tasks. + bigborther_spwan_task(); + //Perform POST + bigborther_initiate_post(); + while (true) { + if (Semaphore_pend(semBigBrotherMsg, BIOS_WAIT_FOREVER)) { + while (!Queue_empty(bigBrotherRxMsgQueue)) { + uint8_t *pWrite = (uint8_t *) Util_dequeueMsg( + bigBrotherRxMsgQueue); + if (pWrite) { + bigbrother_process_rx_msg(pWrite); + } + } + while (!Queue_empty(bigBrotherTxMsgQueue)) { + uint8_t *pWrite = (uint8_t *) Util_dequeueMsg( + bigBrotherTxMsgQueue); + if (pWrite) { + bigbrother_process_tx_msg(pWrite); + } + } + } + } +} + +/***************************************************************************** + ** FUNCTION NAME : bigbrother_createtask + ** + ** DESCRIPTION : Creates task for Big Brother task + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void bigbrother_createtask(void) +{ + //watchdog_pin(); + Task_Params taskParams; + // Configure task + Task_Params_init(&taskParams); + taskParams.stack = bigBrotherTaskStack; + taskParams.stackSize = BIGBROTHER_TASK_STACK_SIZE; + taskParams.priority = BIGBROTHER_TASK_PRIORITY; + Task_construct(&bigBrotherTask, bigbrother_taskfxn, &taskParams, NULL); + LOGGER_DEBUG("BIGBROTHER:INFO::Creating a BigBrother task.\n"); +} diff --git a/firmware/psu/src/comm/gossiper.c b/firmware/psu/src/comm/gossiper.c new file mode 100644 index 0000000000..45e2a6e695 --- /dev/null +++ b/firmware/psu/src/comm/gossiper.c @@ -0,0 +1,248 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include "comm/gossiper.h" +#include "common/inc/global/ocmp_frame.h" +#include "inc/common/bigbrother.h" +#include "inc/common/global_header.h" +#include "inc/interfaces/uartdma.h" + +#include +#include + +/***************************************************************************** + * HANDLES DEFINITION + *****************************************************************************/ +// Semaphore on which USB will listen. +//extern Semaphore_Handle semUSBTX; + +/* + * usbTxMsgQueue - Message queue of USB interface to read. + * Will be filled in by Gossiper. + */ +//extern Queue_Handle usbTxMsgQueue; + +//extern Semaphore_Handle ethTxsem; +//extern Queue_Handle ethTxMsgQueue; + +/* Config message Queue */ +/* + * This is the semaphore posted by either Interface (UART/Ethernet) when it has + * recieved from external world or Bigbrother task once frame is processed and + * needs to be sent back. + */ +Semaphore_Handle semGossiperMsg; + +// Queue object +static Queue_Struct gossiperRxMsg; +static Queue_Struct gossiperTxMsg; + +/* + * gossiperRxMsgQueue - Queue used by the Interface to + * send data to Gossiper + */ +Queue_Handle gossiperRxMsgQueue; + +/* + * gossiperTxMsgQueue - Queue used by the Big brother to + * forward data to Gossiper + */ +Queue_Handle gossiperTxMsgQueue; + +/* Global Task Configuration Variables */ +static Task_Struct gossiperTask; +static Char gossiperTaskStack[GOSSIPER_TASK_STACK_SIZE]; + +/***************************************************************************** + * FUNCTION DECLARATIONS + *****************************************************************************/ +static void gossiper_init(); +static void gossiper_taskfxn(UArg a0, UArg a1); +static ReturnStatus gossiper_process_rx_msg(uint8_t *pMsg); +static ReturnStatus gossiper_process_tx_msg(uint8_t *pMsg); +static ReturnStatus gossiper_uart_send_msg(uint8_t *pMsg); + +/***************************************************************************** + ** FUNCTION NAME : gossiper_createtask + ** + ** DESCRIPTION : Creates task for Gossiper + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void gossiper_createtask(void) +{ + Task_Params taskParams; + // Configure task + Task_Params_init(&taskParams); + taskParams.stack = gossiperTaskStack; + taskParams.stackSize = GOSSIPER_TASK_STACK_SIZE; + taskParams.priority = GOSSIPER_TASK_PRIORITY; + Task_construct(&gossiperTask, gossiper_taskfxn, &taskParams, NULL); + LOGGER_DEBUG("GOSSIPER:INFO::Creating a Gossiper task.\n"); +} + +/***************************************************************************** + ** FUNCTION NAME : gossiper_init + ** + ** DESCRIPTION : Initializes the gossiper task. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void gossiper_init(void) +{ + /*Creating Semaphore for RX Message Queue*/ + semGossiperMsg = Semaphore_create(0, NULL, NULL); + if (semGossiperMsg == NULL) { + LOGGER_ERROR("GOSSIPER:ERROR::GOSSIPER RX Semaphore creation failed.\n"); + } + + /*Creating RX Message Queue*/ + gossiperRxMsgQueue = Util_constructQueue(&gossiperRxMsg); + LOGGER_DEBUG("GOSSIPER:INFO::Constructing message Queue 0x%x for RX Gossiper Messages.\n", + gossiperRxMsgQueue); + + /*Creating TX Message Queue*/ + gossiperTxMsgQueue = Util_constructQueue(&gossiperTxMsg); + LOGGER_DEBUG("GOSSIPER:INFO::Constructing message Queue 0x%x for TX Gossiper Messages.\n", + gossiperTxMsgQueue); +} + +/***************************************************************************** + ** FUNCTION NAME : gossiper_taskfxn + ** + ** DESCRIPTION : Recieve and transmitts media depenedent messages. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void gossiper_taskfxn(UArg a0, UArg a1) +{ + gossiper_init(); + while (true) { + if (Semaphore_pend(semGossiperMsg, BIOS_WAIT_FOREVER)) { + /* Gossiper RX Messgaes */ + while (!Queue_empty(gossiperRxMsgQueue)) { + uint8_t *pWrite = (uint8_t *) Util_dequeueMsg( + gossiperRxMsgQueue); + if (pWrite) { + gossiper_process_rx_msg(pWrite); + } else { + LOGGER_ERROR("GOSSIPER::ERROR:: No Valid Pointer.\n"); + } + } + + /* Gossiper TX Messgaes */ + while (!Queue_empty(gossiperTxMsgQueue)) { + uint8_t *pWrite = (uint8_t *) Util_dequeueMsg( + gossiperTxMsgQueue); + if (pWrite) { + gossiper_process_tx_msg(pWrite); + } else { + LOGGER_ERROR("GOSSIPER::ERROR:: No Valid Pointer.\n"); + } + } + } + } +} + +/***************************************************************************** + ** FUNCTION NAME : gossiper_process_rx_msg + ** + ** DESCRIPTION : Processes the RX Gossiper messages + ** + ** ARGUMENTS : Pointer to message structure + ** + ** RETURN TYPE : RETURN_OK or RETURN_NOTOK + ** + *****************************************************************************/ +static ReturnStatus gossiper_process_rx_msg(uint8_t *pMsg) +{ + ReturnStatus status = RETURN_OK; + LOGGER_DEBUG("GOSSIPER:INFO:: Processing Gossiper RX Message.\n"); + + OCMPMessageFrame * pOCMPMessageFrame = (OCMPMessageFrame *) pMsg; + if (pOCMPMessageFrame != NULL) { + LOGGER_DEBUG("GOSSIPER:INFO:: RX Msg recieved with Length: 0x%x, Interface: 0x%x, Seq.No: 0x%x, TimeStamp: 0x%x.\n", + pOCMPMessageFrame->header.ocmpFrameLen, + pOCMPMessageFrame->header.ocmpInterface, + pOCMPMessageFrame->header.ocmpSeqNumber, + pOCMPMessageFrame->header.ocmpTimestamp); + /*Update the Debug info required based on the debug jumper connected*/ + //status = CheckDebugEnabled() + if (pOCMPMessageFrame->message.msgtype == OCMP_MSG_TYPE_DEBUG) { +#if 0 + if (!IN_DEBUGMODE()) { + // If board is not set in debug mode then discard the message. + } else { + pOCMPMessageFrame->message.msgtype = UNSET_DEBUG_MODE( + pOCMPMessageFrame->message.msgtype); + } +#endif + } + Util_enqueueMsg(bigBrotherRxMsgQueue, semBigBrotherMsg, (uint8_t*) pMsg); + } else { + LOGGER_ERROR("GOSSIPER:ERROR:: Not valid pointer.\n"); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : gossiper_process_tx_msg + ** + ** DESCRIPTION : Processes the Gossiper TX Messages + ** + ** ARGUMENTS : Pointer to message structure + ** + ** RETURN TYPE : RETURN_OK or RETURN_NOTOK + ** + *****************************************************************************/ +static ReturnStatus gossiper_process_tx_msg(uint8_t *pMsg) +{ + ReturnStatus status = RETURN_OK; + LOGGER_DEBUG("GOSSIPER:INFO:: Processing Gossiper TX Message.\n"); + OCMPMessageFrame * pOCMPMessageFrame = (OCMPMessageFrame *) pMsg; + if (pOCMPMessageFrame != NULL) { + status = gossiper_uart_send_msg(pMsg); + } else { + LOGGER_ERROR("BIGBROTHER:ERROR:: Not valid pointer.\n"); + } + return status; +} +/***************************************************************************** + ** FUNCTION NAME : gossiper_uart_send_msg + ** + ** DESCRIPTION : transmitt TX Messages to UART + ** + ** ARGUMENTS : Pointer to message + ** + ** RETURN TYPE : RETURN_OK or RETURN_NOTOK + ** + *****************************************************************************/ +static ReturnStatus gossiper_uart_send_msg(uint8_t *pMsg) +{ + ReturnStatus status = RETURN_OK; + LOGGER_DEBUG("GOSSIPER:INFO:: Forwarding TX message to the UART Interface.\n"); + if (pMsg != NULL) { + Util_enqueueMsg(uartTxMsgQueue, semUARTTX, (uint8_t*) pMsg); + } else { + LOGGER_ERROR("GOSSIPER::ERROR::No Valid Pointer.\n"); + } + return status; +} diff --git a/firmware/psu/src/comm/gossiper.h b/firmware/psu/src/comm/gossiper.h new file mode 100644 index 0000000000..1fd6672765 --- /dev/null +++ b/firmware/psu/src/comm/gossiper.h @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#ifndef GOSSIPER_H_ +#define GOSSIPER_H_ + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include "inc/utils/util.h" + +/***************************************************************************** + * MACRO DEFINITIONS + *****************************************************************************/ +#define GOSSIPER_TASK_PRIORITY 6 +#define GOSSIPER_TASK_STACK_SIZE 2048 + +#define SET_DEBEUG_MODE(debugMode) ((debugMode | 0x00)) +#define UNSET_DEBUG_MODE(debugMode) ((debugMode & 0x0f)) + +/***************************************************************************** + * HANDLE DEFINITIONS + *****************************************************************************/ +/* Semaphore and Queue Handles for Gossiper */ +extern Semaphore_Handle semGossiperMsg; +extern Queue_Handle gossiperTxMsgQueue; +extern Queue_Handle gossiperRxMsgQueue; + +/***************************************************************************** + * FUNCTION DECLARATIONS + *****************************************************************************/ +void gossiper_createtask(void); + +#endif /* GOSSIPER_H_ */ diff --git a/firmware/psu/src/devices/eeprom.c b/firmware/psu/src/devices/eeprom.c new file mode 100644 index 0000000000..0ef0a88f40 --- /dev/null +++ b/firmware/psu/src/devices/eeprom.c @@ -0,0 +1,267 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** +#include "Board.h" +#include "inc/common/global_header.h" +#include "inc/common/byteorder.h" +#include "inc/devices/eeprom.h" + +#include +#ifndef UT_FRAMEWORK +#include /* TODO: for htons - clean up this random include */ +#endif +#include + +#define WP_ASSERT 1 +#define WP_DEASSERT 0 + +extern Eeprom_Cfg eeprom_psu_sid; +extern Eeprom_Cfg eeprom_psu_inv; + +static ReturnStatus i2c_eeprom_write(I2C_Handle i2cHandle, + uint8_t deviceAddress, + uint16_t regAddress, + const void *value, + size_t numofBytes); + +static ReturnStatus i2c_eeprom_read(I2C_Handle i2cHandle, + uint16_t deviceAddress, + uint16_t regAddress, + void *value, + size_t numofbytes); + +/***************************************************************************** + ** FUNCTION NAME : eeprom_init + ** + ** DESCRIPTION : Initialize an EEPROM device (WP gpio / test read) + ** + ** ARGUMENTS : EEPROM config + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +bool eeprom_init(Eeprom_Cfg *cfg) { + /* Configure our WP pin (if any) and set to be low (protected) by default */ + if (cfg->pin_wp) { + OcGpio_configure(cfg->pin_wp, + OCGPIO_CFG_OUTPUT | OCGPIO_CFG_OUT_HIGH); + } + + /* Test communication to the EEPROM */ + uint8_t test_byte; + if (eeprom_read(cfg, 0x00, &test_byte, sizeof(test_byte)) != RETURN_OK) { + return false; + } + + return true; +} + +/***************************************************************************** + ** FUNCTION NAME : eeprom_read + ** + ** DESCRIPTION : Read the values from the EEPROM register. + ** + ** ARGUMENTS : EEPROM (Slave) address, Register address and + ** pointer to value read. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus eeprom_read(Eeprom_Cfg *cfg, + uint16_t address, + void *buffer, + size_t size) +{ + ReturnStatus status = RETURN_OK; + I2C_Handle eepromHandle = i2c_get_handle(cfg->i2c_dev.bus); + if (!eepromHandle) { + LOGGER_ERROR("EEPROM:ERROR:: Failed to get I2C Bus for " + "EEPROM device 0x%x.\n", cfg->i2c_dev.slave_addr); + } else { + /* TODO: if we're concerned about hogging the bus, we could always + * page reads, but this doesn't seem necessary right now + */ + /* TODO: check for out-of-bounds addresses (some EEPROM wrap around + * when reading after the end, so this could lead to confusion) + */ + status = i2c_eeprom_read(eepromHandle, cfg->i2c_dev.slave_addr, + address, buffer, size); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : eeprom_write + ** + ** DESCRIPTION : Write the value to EEPROM register. + ** + ** ARGUMENTS : EEPROM (Slave) address, Register address and value + ** to be written. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus eeprom_write(const Eeprom_Cfg *cfg, + uint16_t address, + const void *buffer, + size_t size) +{ + ReturnStatus status = RETURN_OK; + I2C_Handle eepromHandle = i2c_get_handle(cfg->i2c_dev.bus); + if (!eepromHandle) { + LOGGER_ERROR("EEPROM:ERROR:: Failed to get I2C Bus for " + "EEPROM device 0x%x.\n", cfg->i2c_dev.slave_addr); + } else { + /* Respect EEPROM page size */ + const size_t page_size = cfg->type.page_size; + if (page_size) { + while (size > page_size) { + status = i2c_eeprom_write(eepromHandle, cfg->i2c_dev.slave_addr, + address, buffer, page_size); + + size -= page_size; + address += page_size; + buffer = (const uint8_t *)buffer + page_size; + } + } + status = i2c_eeprom_write(eepromHandle, cfg->i2c_dev.slave_addr, + address, buffer, size); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : i2c_eeprom_write + ** + ** DESCRIPTION : Writing device register over i2c bus. + ** + ** ARGUMENTS : I2C handle, device address, register address and value. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus i2c_eeprom_write(I2C_Handle i2cHandle, + uint8_t slaveAddress, + uint16_t memAddress, + const void *value, + size_t numofBytes) +{ + ReturnStatus status = RETURN_OK; + uint8_t txBuffer[numofBytes + 1]; + + /*TODO: This approach needs to be looked into when same + * codebase is used for PSU and GBC + + uint8_t txBuffer[numofBytes + 2]; + *(uint16_t *)txBuffer = htobe16(memAddress); + memcpy((txBuffer + 2), value, numofBytes); + */ + + *txBuffer = (memAddress & 0xFF); + slaveAddress |= ((memAddress >> 8 ) & 0x01); + memcpy((txBuffer + 1), value, numofBytes); + I2C_Transaction i2cTransaction; + i2cTransaction.slaveAddress = slaveAddress; + i2cTransaction.writeBuf = txBuffer; + i2cTransaction.writeCount = sizeof(txBuffer); + i2cTransaction.readBuf = NULL; + i2cTransaction.readCount = 0; + if (I2C_transfer(i2cHandle, &i2cTransaction)) { + LOGGER_DEBUG("EEPROM:INFO:: I2C write success for device: 0x%x reg Addr: 0x%x\n", + slaveAddress, memAddress); + status = RETURN_OK; + } else { + LOGGER_ERROR("EEPROM:ERROR:: I2C write failed for for device: 0x%x reg Addr: 0x%x\n", + slaveAddress, memAddress); + status = RETURN_NOTOK; + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : i2c_eeprom_read + ** + ** DESCRIPTION : Reading device register over i2c bus. + ** + ** ARGUMENTS : I2C handle, device address, register address and value. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus i2c_eeprom_read(I2C_Handle i2cHandle, + uint16_t slaveAddress, + uint16_t memAddress, + void *value, + size_t numofbytes) +{ + ReturnStatus status = RETURN_OK; +// uint16_t txBuffer = htobe16(memAddress); /* Address is big-endian */ + uint8_t txBuffer = (memAddress & 0xFF); /* Address is big-endian */ + slaveAddress |= ((memAddress >> 8 )& 0x01); + + I2C_Transaction i2cTransaction; + i2cTransaction.slaveAddress = slaveAddress; + i2cTransaction.writeBuf = &txBuffer; + i2cTransaction.writeCount = sizeof(txBuffer); + i2cTransaction.readBuf = value; + i2cTransaction.readCount = numofbytes; + if (I2C_transfer(i2cHandle, &i2cTransaction)) { + LOGGER_DEBUG("EEPROM:INFO:: I2C read success for device: 0x%x reg Addr: 0x%x\n", + slaveAddress, memAddress); + status = RETURN_OK; + } else { + LOGGER_ERROR("EEPROM:ERROR:: I2C write failed for for device: 0x%x reg Addr: 0x%x\n", + slaveAddress, memAddress); + status = RETURN_NOTOK; + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : eeprom_disable_write + ** + ** DESCRIPTION : Read the values from the EEPROM register. + ** + ** ARGUMENTS : EEPROM handle. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus eeprom_disable_write(Eeprom_Cfg *cfg) +{ + if (cfg->pin_wp) { + OcGpio_write(cfg->pin_wp, WP_ASSERT); + } + + /* TODO: error detection */ + return RETURN_OK; +} + +/***************************************************************************** + ** FUNCTION NAME : eeprom_enable_write + ** + ** DESCRIPTION : Enable eeprom write operation. + ** + ** ARGUMENTS : EEPROM handle. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus eeprom_enable_write(Eeprom_Cfg *cfg) +{ + if (cfg->pin_wp) { + OcGpio_write(cfg->pin_wp, WP_DEASSERT); + } + + /* TODO: error detection */ + return RETURN_OK; +} diff --git a/firmware/psu/src/devices/i2c/threaded_int.c b/firmware/psu/src/devices/i2c/threaded_int.c new file mode 100644 index 0000000000..5047cd7282 --- /dev/null +++ b/firmware/psu/src/devices/i2c/threaded_int.c @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#include "threaded_int.h" + +#include "inc/common/global_header.h" + +#include +#include +#include + +// Threaded interrupt info +//#define TI_TASKSTACKSIZE 1024 +#define TI_TASKSTACKSIZE 512 +#define TI_TASKPRIORITY 6 + +// This number is fairly superficial - just used to keep track of the +// various tasks, it can be increased without much overhead +#define MAX_DEVICES 30 + +// Config simply to map context to our GPIO interrupts +typedef struct InterruptConfig { + Semaphore_Handle sem; //!< Semaphore to wake up INT thread + ThreadedInt_Callback cb; //!< Callback to run when interrupt occurs + void *context; //!< Pointer to pass to cb function +} InterruptConfig; +static InterruptConfig s_intConfigs[MAX_DEVICES] = {}; +static int s_numDevices = 0; + +static void gpioIntFxn(const OcGpio_Pin *pin, void *context) { + Semaphore_Handle sem = context; + + // TODO: this should probably be an assert + if (!sem) { + return; + } + + /* Just wake up the TI task */ + Semaphore_post(sem); +} + +static void ThreadedInt_Task(UArg arg0, UArg arg1) { + InterruptConfig *cfg = (InterruptConfig *)arg0; + if (!cfg) { + DEBUG("Threaded Int started without configuration???\n"); + return; + } + + DEBUG("Threaded INT thread ready\n"); + while (true) { + Semaphore_pend(cfg->sem, BIOS_WAIT_FOREVER); + cfg->cb(cfg->context); + } +} + +// TODO: this function isn't thread safe at the moment +void ThreadedInt_Init(OcGpio_Pin *irqPin, ThreadedInt_Callback cb, + void *context) { + /*TODO: stop gap arrangement to prevent spawning tasks for monitoring alerts*/ + // return; + // Build up table of all devices for interrupt handling. This is an ok + // workaround for TI RTOS GPIO interrupts for now (only using one device) + if (s_numDevices >= MAX_DEVICES) { + DEBUG("ThrdInt::FATAL: too many configurations"); + return; + } + int devNum = s_numDevices++; + + Semaphore_Handle sem = Semaphore_create(0, NULL, NULL); + if (!sem) { + DEBUG("ThrdInt::Can't create ISR semaphore\n"); + return; + } + + s_intConfigs[devNum] = (InterruptConfig) { + .sem = sem, + .cb = cb, + .context = context, + }; + + // Start interrupt handling task + // One task per interrupt, not that efficient, but we don't have much + // need to optimize into a thread pool + // TODO: look into error block and see if I should use it + Task_Params taskParams; + Task_Params_init(&taskParams); + taskParams.stackSize = TI_TASKSTACKSIZE; + taskParams.priority = TI_TASKPRIORITY; + taskParams.arg0 = (uintptr_t)&s_intConfigs[devNum]; + Task_Handle task = Task_create(ThreadedInt_Task, &taskParams, NULL); + if (!task) { + DEBUG("ThrdInt::FATAL: Unable to start interrupt task\n"); + Semaphore_delete(&sem); + s_numDevices--; + return; + } + // TODO: what do I do with task handle? + + /* Set up IRQ pin callback */ + OcGpio_setCallback(irqPin, gpioIntFxn, sem); + OcGpio_enableInt(irqPin); +} + diff --git a/firmware/psu/src/devices/i2c/threaded_int.h b/firmware/psu/src/devices/i2c/threaded_int.h new file mode 100644 index 0000000000..891b77587e --- /dev/null +++ b/firmware/psu/src/devices/i2c/threaded_int.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#pragma once + +#ifndef DEVICES_I2C_THREADED_INT_H_ +#define DEVICES_I2C_THREADED_INT_H_ + +#include "drivers/OcGpio.h" + +typedef void (*ThreadedInt_Callback)(void *context); + +void ThreadedInt_Init(OcGpio_Pin *irqPin, ThreadedInt_Callback cb, + void *context); + +#endif /* DEVICES_I2C_THREADED_INT_H_ */ diff --git a/firmware/psu/src/devices/i2cbus.c b/firmware/psu/src/devices/i2cbus.c new file mode 100644 index 0000000000..81c5e95d50 --- /dev/null +++ b/firmware/psu/src/devices/i2cbus.c @@ -0,0 +1,151 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** +#include "Board.h" +#include "helpers/array.h" +#include "inc/common/global_header.h" +#include "inc/common/i2cbus.h" + +/* TI-RTOS driver files */ +#include + +#include + +//***************************************************************************** +// HANDLES DEFINITION +//***************************************************************************** +I2C_Handle OC_I2C_Handle[Board_I2CCOUNT] = {}; + +/***************************************************************************** + ** FUNCTION NAME : i2c_open_bus + ** + ** DESCRIPTION : Initialize I2C Bus + ** + ** ARGUMENTS : I2C bus index + ** + ** RETURN TYPE : I2C_Handle (NULL on failure) + ** + *****************************************************************************/ +I2C_Handle i2c_open_bus(unsigned int index) +{ + if (index >= ARRAY_SIZE(OC_I2C_Handle)) { + LOGGER_ERROR("I2CBUS:ERROR:: I2C bus %d not found\n", index); + return NULL; + } + + I2C_Params i2cParams; + I2C_Params_init(&i2cParams); + i2cParams.bitRate = I2C_400kHz; + + if (!OC_I2C_Handle[index]) { + OC_I2C_Handle[index] = I2C_open(index, &i2cParams); + + if (!OC_I2C_Handle[index]) { + LOGGER_ERROR("I2CBUS:ERROR:: Failed Initializing I2C bus %d.\n", + index); + } + } + return OC_I2C_Handle[index]; +} + +/***************************************************************************** + ** FUNCTION NAME : i2c_close_bus + ** + ** DESCRIPTION : Initialize I2C Bus + ** + ** ARGUMENTS : I2C bus index + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void i2c_close_bus(I2C_Handle* i2cHandle) +{ + I2C_close(*i2cHandle); + i2cHandle = NULL; +} + +/***************************************************************************** + ** FUNCTION NAME : i2c_reg_write + ** + ** DESCRIPTION : Writing device register over i2c bus. + ** + ** ARGUMENTS : I2C handle, device address, register address and value. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus i2c_reg_write( I2C_Handle i2cHandle, + uint8_t deviceAddress, + uint8_t regAddress, + uint16_t value, + uint8_t numofBytes) +{ + ReturnStatus status = RETURN_OK; + uint8_t txBuffer[3]; + I2C_Transaction i2cTransaction; + txBuffer[0] = regAddress; + memcpy(&txBuffer[1],&value,numofBytes); + i2cTransaction.slaveAddress = deviceAddress; + i2cTransaction.writeBuf = txBuffer; + i2cTransaction.writeCount = numofBytes + 1; + i2cTransaction.readBuf = NULL; + i2cTransaction.readCount = 0; + if (I2C_transfer(i2cHandle, &i2cTransaction)) { + //LOGGER_DEBUG("I2CBUS:INFO:: I2C write success for device: 0x%x reg Addr: 0x%x value: 0x%x.\n", + // deviceAddress, regAddress, value); + status = RETURN_OK; + } else { + LOGGER_ERROR("I2CBUS:ERROR:: I2C write failed for for device: 0x%x reg Addr: 0x%x value: 0x%x.\n", + deviceAddress, regAddress, value); + status = RETURN_NOTOK; + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : i2c_reg_read + ** + ** DESCRIPTION : Reading device register over i2c bus. + ** + ** ARGUMENTS : I2C handle, device address, register address and value. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus i2c_reg_read( I2C_Handle i2cHandle, + uint8_t deviceAddress, + uint8_t regAddress, + uint16_t *value, + uint8_t numofBytes) +{ + ReturnStatus status = RETURN_OK; + uint8_t txBuffer[1] = { 0 }; + uint8_t rxBuffer[2] = { 0 }; + txBuffer[0] = regAddress; + I2C_Transaction i2cTransaction; + i2cTransaction.slaveAddress = deviceAddress; + i2cTransaction.writeBuf = &txBuffer; + i2cTransaction.writeCount = 1; + i2cTransaction.readBuf = rxBuffer; + i2cTransaction.readCount = numofBytes; + if (I2C_transfer(i2cHandle, &i2cTransaction)) { + memcpy(value,rxBuffer,numofBytes); + LOGGER_ERROR("I2CBUS:INFO:: I2C read success for device: 0x%x reg Addr: 0x%x value : 0x%x.\n", + deviceAddress, regAddress, *value); + status = RETURN_OK; + } else { + LOGGER_ERROR("I2CBUS:ERROR:: I2C read failed for for device: 0x%x reg Addr: 0x%x.\n", + deviceAddress, regAddress); + status = RETURN_NOTOK; + } + return status; +} diff --git a/firmware/psu/src/devices/ina226.c b/firmware/psu/src/devices/ina226.c new file mode 100644 index 0000000000..fad9e080fd --- /dev/null +++ b/firmware/psu/src/devices/ina226.c @@ -0,0 +1,596 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** +#include "devices/i2c/threaded_int.h" +#include "inc/common/byteorder.h" +#include "inc/common/global_header.h" +#include "inc/devices/ina226.h" +#include "helpers/memory.h" + +/***************************************************************************** + * REGISTER DEFINITIONS + *****************************************************************************/ +#define INA_CONFIGURATION_REG 0x00 +#define INA_SHUNTVOLTAGE_REG 0x01 +#define INA_BUSVOLTAGE_REG 0x02 +#define INA_POWER_REG 0x03 +#define INA_CURRENT_REG 0x04 +#define INA_CALIBRATION_REG 0x05 +#define INA_MASKENABLE_REG 0x06 +#define INA_ALERTLIMIT_REG 0x07 +#define INA_MANUFACTUREID_REG 0xFE +#define INA_DIEID_REG 0xFF + +/*INA226 Device Info */ +#define INA226_MANFACTURE_ID 0x5449 +#define INA226_DEVICE_ID 0x2260 +#define INA226_DEV_VERSION 0x00 + +/* Configuration Register Bits */ +#define INA_CFG_RESET (1 << 15) + +/* + * Conversion of current into Shunt Voltage Register contents and viceversa. + * + * First Calculate the Current Register Value from the given Current Value + * ui16rfINARegValue = ui16rfINACurrentLimit/(INA226_CURRENT_LSB); + * Calculate Shunt Voltage Alert Limit Register Value + * ui16rfINARegValue = (ui16rfINARegValue * 2048)/INA226_CALIBRATION_REG_VALUE; + */ +#define CURRENT_TO_REG(x) ((2048 *(x/INA226_CURRENT_LSB)/INA226_CAL_REG_VALUE)) +#define REG_TO_CURRENT(y) ((y * INA226_CURRENT_LSB * INA226_CAL_REG_VALUE)/2048) + +/***************************************************************************** + * CONSTANTS DEFINITIONS + *****************************************************************************/ +/* INA226 LSB Values */ +#define INA226_VSHUNT_LSB 2.5 /* 2.5uV or 2500nV (uV default) */ +#define INA226_VBUS_LSB 1.25 /* 1.25mV or 1250uV (mV default) */ +//#define INA226_CURRENT_LSB 0.1 /* 0.100mA 0r 100uA (mA default) */ +#define INA226_CURRENT_LSB 0.00205 /* 0.100mA 0r 100uA (mA default) */ +#define INA226_POWER_LSB 2.5 /* 2.5mW or 2500uW (mW default) */ + +/* Configure the Configuration register with Number of Samples and Conversion + * Time for Shunt and Bus Voltage. + * Min(Default):0x4127; Max: 0x4FFF; Average: 0x476F + */ +#define INA226_CONFIG_REG_VALUE 0x476F + +/* Configure Calibration register with shunt resistor value and current LSB. + Current_LSB = Maximum Expected Current/2^15 + Current_LSB = 2A/2^15 = 0.00006103515625 = 61uA ~ 100uA(Maximum Expected Current = 2A) + Calibration Register(CAL) = 0.00512/(Current_LSB*RSHUNT) + CAL = 0.00512/(100uA*2mOhm) = = 25600 = 0x6400.(RSHUNT = 2mohm) + */ +//#define INA226_CAL_REG_VALUE 0x6400 +#define INA226_CAL_REG_VALUE 0xA3D7 + +#define INA226_MASKEN_REG_VALUE 0x8001 + +extern __attribute__((weak)) OcGpio_Port ec_io; + +void watchdog_pin() +{ + OcGpio_Pin pin_watchdog = { &ec_io, 2 }; + + const uint32_t pin_evt_cfg = 0; + OcGpio_configure(&pin_watchdog, pin_evt_cfg); + while(1) { + OcGpio_write(&pin_watchdog, true); + //Task_sleep(4*500); + OcGpio_write(&pin_watchdog, false); + } +return; +} +/***************************************************************************** + ** FUNCTION NAME : read_ina_reg + ** + ** DESCRIPTION : Read a 16 bit value from INA226 register. + ** + ** ARGUMENTS : i2c device, Register address and value + ** to be read. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus read_ina_reg(const INA226_Dev *dev, + uint8_t regAddress, + uint16_t *regValue) +{ + ReturnStatus status = RETURN_NOTOK; + I2C_Handle inaHandle = i2c_get_handle(dev->cfg.dev.bus); + if (!inaHandle) { + LOGGER_ERROR("INASENSOR:ERROR:: Failed to get I2C Bus for INA sensor " + "0x%x on bus 0x%x.\n", dev->cfg.dev.slave_addr, + dev->cfg.dev.bus); + } else { + status = i2c_reg_read(inaHandle, dev->cfg.dev.slave_addr, regAddress, + regValue, 2); + *regValue = betoh16(*regValue); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : write_ina_reg + ** + ** DESCRIPTION : Write 16 bit value to INA226 register. + ** + ** ARGUMENTS : i2c device, Register address and value + ** to be written. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus write_ina_reg(const INA226_Dev *dev, + uint8_t regAddress, + uint16_t regValue) +{ + ReturnStatus status = RETURN_NOTOK; + I2C_Handle inaHandle = i2c_get_handle(dev->cfg.dev.bus); + if (!inaHandle) { + LOGGER_ERROR("INASENSOR:ERROR:: Failed to get I2C Bus for INA sensor " + "0x%x on bus 0x%x.\n", dev->cfg.dev.slave_addr, + dev->cfg.dev.bus); + } else { + regValue = htobe16(regValue); + status = i2c_reg_write(inaHandle, dev->cfg.dev.slave_addr, regAddress, + regValue, 2); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_get_dev_id + ** + ** DESCRIPTION : Read the device id of Current sensor. + ** + ** ARGUMENTS : i2c device and pointer to device Id. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus ina226_getDevId(INA226_Dev *dev, uint16_t *devID) +{ + return read_ina_reg(dev, INA_DIEID_REG, devID); +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_get_mfg_id + ** + ** DESCRIPTION : Read the mfg id of Current sensor. + ** + ** ARGUMENTS : i2c device and out-pointer to manufacturing ID. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus ina226_getMfgId(INA226_Dev *dev, uint16_t *mfgID) +{ + return read_ina_reg(dev, INA_MANUFACTUREID_REG, mfgID); +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_set_cfg_reg + ** + ** DESCRIPTION : Write the value to Current sensor configuration + ** register. + ** + ** ARGUMENTS : i2c device and new value of configuration register. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus _set_cfg_reg(INA226_Dev *dev, uint16_t regValue) +{ + return write_ina_reg(dev, INA_CONFIGURATION_REG, regValue); +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_set_cal_reg + ** + ** DESCRIPTION : Write the value to Current sensor calibration register. + ** + ** ARGUMENTS : i2c device and new value of calibration register. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus _set_cal_reg(INA226_Dev *dev, uint16_t regValue) +{ + return write_ina_reg(dev, INA_CALIBRATION_REG, regValue); +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_get_curr_limit + ** + ** DESCRIPTION : Read the value of Current sensor alert limit register. + ** + ** ARGUMENTS : i2c device and out-pointer to current limit. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ina226_readCurrentLim(INA226_Dev *dev, uint16_t* currLimit) +{ + uint16_t regValue = 0x0000; + ReturnStatus status = read_ina_reg(dev, INA_ALERTLIMIT_REG, ®Value); + if (status == RETURN_OK) { + *currLimit = REG_TO_CURRENT(regValue); + LOGGER_DEBUG("INASENSOR:INFO:: INA sensor 0x%x on bus 0x%x is " + "reporting current limit of %d mA.\n", + dev->cfg.dev.slave_addr, dev->cfg.dev.bus, *currLimit); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_set_curr_limit + ** + ** DESCRIPTION : Write the value to Current sensor alert limit register. + ** + ** ARGUMENTS : i2c device and new current limit. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ina226_setCurrentLim(INA226_Dev *dev, uint16_t currLimit) +{ + uint16_t regValue = CURRENT_TO_REG(currLimit); + return write_ina_reg(dev, INA_ALERTLIMIT_REG, regValue); +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_read_alert_reg + ** + ** DESCRIPTION : Read the value to Current sensor mask/enable register. + ** + ** ARGUMENTS : i2c device and out-pointer to enable and mask bits. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus _read_alert_reg(INA226_Dev *dev, uint16_t* regValue) +{ + return read_ina_reg(dev, INA_MASKENABLE_REG, regValue); +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_enable_alert + ** + ** DESCRIPTION : Write the value to Current sensor mask/enable register. + ** + ** ARGUMENTS : i2c device and alert to be enabled. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus _enable_alert(INA226_Dev *dev, uint16_t regValue) +{ + return write_ina_reg(dev, INA_MASKENABLE_REG, regValue); +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_get_bus_volt_value + ** + ** DESCRIPTION : Read the value of Current sensor bus voltage value. + ** + ** ARGUMENTS : i2c device and out-pointer to bus voltage value. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ina226_readBusVoltage(INA226_Dev *dev, + uint16_t* busVoltValue) +{ + uint16_t regValue; + ReturnStatus status = read_ina_reg(dev, INA_BUSVOLTAGE_REG, ®Value); + + if (status == RETURN_OK) { + *busVoltValue = regValue * INA226_VBUS_LSB; + LOGGER_DEBUG("INASENSOR:INFO:: INA sensor 0x%x on bus 0x%x is " + "reporting bus voltage value of %d mV.\n", + dev->cfg.dev.slave_addr, dev->cfg.dev.bus, *busVoltValue); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_get_shunt_volt_value + ** + ** DESCRIPTION : Read the value of Current sensor shunt voltage value. + ** + ** ARGUMENTS : i2c device and out-pointer to shunt voltage. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ina226_readShuntVoltage(INA226_Dev *dev, + uint16_t* shuntVoltValue) +{ + uint16_t regValue; + ReturnStatus status = read_ina_reg(dev, INA_SHUNTVOLTAGE_REG, ®Value); + + if (status == RETURN_OK) { + *shuntVoltValue = regValue * INA226_VSHUNT_LSB; + LOGGER_DEBUG("INASENSOR:INFO:: INA sensor 0x%x on bus 0x%x is " + "reporting shunt voltage value of %d uV.\n", + dev->cfg.dev.slave_addr, dev->cfg.dev.bus, *shuntVoltValue); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_get_curr_value + ** + ** DESCRIPTION : Read the value of Current sensor current value. + ** + ** ARGUMENTS : i2c device and out-pointer to current value. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ina226_readCurrent(INA226_Dev *dev, uint16_t* currValue) +{ + uint16_t regValue; + ReturnStatus status = read_ina_reg(dev, INA_CURRENT_REG, ®Value); + + if (status == RETURN_OK) { + *currValue = regValue * INA226_CURRENT_LSB; + LOGGER_DEBUG("INASENSOR:INFO:: INA sensor 0x%x on bus 0x%x " + "is reporting current value of %d mA.\n", + dev->cfg.dev.slave_addr, dev->cfg.dev.bus, *currValue); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_get_power_value + ** + ** DESCRIPTION : Read the value of Current sensor power value. + ** + ** ARGUMENTS : i2c device and out-pointer to power value. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ina226_readPower(INA226_Dev *dev, uint16_t* powValue) +{ + uint16_t regValue; + ReturnStatus status = read_ina_reg(dev, INA_POWER_REG, ®Value); + if (status == RETURN_OK) { + *powValue = regValue * INA226_POWER_LSB; + LOGGER_DEBUG("INASENSOR:INFO:: INA sensor 0x%x on bus 0x%x is " + "reporting power value of %d mV.\n", + dev->cfg.dev.slave_addr, dev->cfg.dev.bus, *powValue); + } + return status; +} + +/***************************************************************************** + * Internal IRQ handler - reads in triggered interrupts and dispatches CBs + *****************************************************************************/ +static void _ina226_isr(void *context) { + INA226_Dev *dev = context; + + /* Read the alert mask register (will clear the alert bit if set) */ + /* TODO: this seems to be a strange bug in the sensor - sometimes it returns + * 0xFF as the lo-byte. If this occurs, we need to re-read it. + * NOTE: 0x1F is a perfectly legal value, but bits 5-9 are RFU and + * normally zero */ + uint16_t alert_mask = 0xFFFF; + while (LOBYTE(alert_mask) == 0xFF) { + if (_read_alert_reg(dev, &alert_mask) != RETURN_OK) { + LOGGER_DEBUG("INA226:ERROR:: INT mask read failed\n"); + return; + } + } + + if (!dev->obj.alert_cb) { + return; + } + + if (alert_mask & INA_MSK_AFF) { + /* This alert was caused by a fault */ + + /* Theory of operation: After reading the alert, we change the alert + * mask to look at the complement alert. For example, after getting a + * bus over-voltage alert, we switch the mask to tell us when it's + * under-voltage and thus back in operating limits so that it's less + * likely that we'll miss alerts on the shared line, although not + * guaranteed :( */ + + /* The device can only monitor one metric at a time, if multiple flags + * are set, it monitors the highest order bit, so check the config + * in order, from MSB to LSB */ + uint16_t value; + uint16_t new_mask = alert_mask & (~INA_ALERT_EN_MASK); + INA226_Event evt; + uint16_t alert_lim; + ina226_readCurrentLim(dev, &alert_lim); + + if (alert_mask & INA_MSK_SOL) { + if (dev->obj.evt_to_monitor == INA226_EVT_COL || + dev->obj.evt_to_monitor == INA226_EVT_CUL) { + if (ina226_readCurrent(dev, &value) != RETURN_OK) { + value = UINT16_MAX; + } + alert_lim -= INA_HYSTERESIS; + evt = INA226_EVT_COL; + } else { + if (ina226_readShuntVoltage(dev, &value) != RETURN_OK) { + value = UINT16_MAX; + } + evt = INA226_EVT_SOL; + } + new_mask |= INA_MSK_SUL; + } else if (alert_mask & INA_MSK_SUL) { + if (dev->obj.evt_to_monitor == INA226_EVT_CUL || + dev->obj.evt_to_monitor == INA226_EVT_COL) { + if (ina226_readCurrent(dev, &value) != RETURN_OK) { + value = UINT16_MAX; + } + alert_lim += INA_HYSTERESIS; + evt = INA226_EVT_CUL; + } else { + if (ina226_readShuntVoltage(dev, &value) != RETURN_OK) { + value = UINT16_MAX; + } + evt = INA226_EVT_SUL; + } + new_mask |= INA_MSK_SOL; + } else if (alert_mask & INA_MSK_BOL) { + if (ina226_readBusVoltage(dev, &value) != RETURN_OK) { + value = UINT16_MAX; + } + evt = INA226_EVT_BOL; + new_mask |= INA_MSK_BUL; + } else if (alert_mask & INA_MSK_BUL) { + if (ina226_readBusVoltage(dev, &value) != RETURN_OK) { + value = UINT16_MAX; + } + evt = INA226_EVT_BUL; + new_mask |= INA_MSK_BOL; + } else if (alert_mask & INA_MSK_POL) { + if (ina226_readPower(dev, &value) != RETURN_OK) { + value = UINT16_MAX; + } + evt = INA226_EVT_POL; + /* TODO: there isn't a PUL alert, not sure what to do here. We + * don't currently use this alert, but it would be nice to have a + * complete driver */ + new_mask |= INA_MSK_POL; + } else { + LOGGER_ERROR("INA226:Unknown alert type\n"); + return; + } + + /* Set a new limit in order to account for hysteresis */ + /* TODO: make this work for all alert types (this is a hack) */ + ina226_setCurrentLim(dev, alert_lim); + + /* Invert the alert type we're looking for */ + if (_enable_alert(dev, new_mask) != RETURN_OK) { + /* TODO [HACK]: this sometimes reports failures at random times, so + * this is a hacked together retry to keep things stable*/ + _enable_alert(dev, new_mask); + } + + dev->obj.alert_cb(evt, value, dev->obj.cb_context); + } + /* TODO: Conversion ready not handled */ +} + +/***************************************************************************** + *****************************************************************************/ +ReturnStatus ina226_init(INA226_Dev *dev) +{ + ReturnStatus status; + dev->obj = (INA226_Obj){}; + + /* Perform a device reset to be safe */ + status = _set_cfg_reg(dev, INA_CFG_RESET); + if (status != RETURN_OK) { + return status; + } + + /* Configure the Configuration register with number of samples and + * conversion time for shunt and bus voltage */ + status = _set_cfg_reg(dev, INA226_CONFIG_REG_VALUE); + if (status != RETURN_OK) { + return status; + } + + /* Configure the Calibration register with shunt resistor value and + * current LSB */ + status = _set_cal_reg(dev, INA226_CAL_REG_VALUE); + if (status != RETURN_OK) { + return status; + } + + /* Make sure we're talking to the right device */ +// if (ina226_probe(dev) != POST_DEV_FOUND) { +// return RETURN_NOTOK; +// } + + if (dev->cfg.pin_alert) { + const uint32_t pin_evt_cfg = OCGPIO_CFG_INPUT | OCGPIO_CFG_INT_FALLING; + if (OcGpio_configure(dev->cfg.pin_alert, pin_evt_cfg) < OCGPIO_SUCCESS) { + return RETURN_NOTOK; + } + + /* Use a threaded interrupt to handle IRQ */ + // ThreadedInt_Init(dev->cfg.pin_alert, _ina226_isr, (void *)dev); + } + return RETURN_OK; +} + +/***************************************************************************** + *****************************************************************************/ +void ina226_setAlertHandler(INA226_Dev *dev, INA226_CallbackFn alert_cb, + void *cb_context) { + dev->obj.alert_cb = alert_cb; + dev->obj.cb_context = cb_context; +} + +/***************************************************************************** + *****************************************************************************/ +ReturnStatus ina226_enableAlert(INA226_Dev *dev, INA226_Event evt) +{ + /* TODO: perhaps caching the mask is better? If we have an active alert, + * we'll inadvertently clear it here */ + /* TODO: this isn't thread safe, but does it need to be? */ + + uint16_t alert_mask; + ReturnStatus res = _read_alert_reg(dev, &alert_mask); + if (res != RETURN_OK) { + return res; + } + + alert_mask &= (~INA_ALERT_EN_MASK); /* Wipe out previous alert EN bits */ + //alert_mask |= (INA_MSK_LEN); /* Enable latch mode (never miss an alert) */ + dev->obj.evt_to_monitor = evt; + switch (evt) { + case INA226_EVT_COL: + alert_mask |= INA_MSK_SOL; + break; + case INA226_EVT_CUL: + alert_mask |= INA_MSK_SUL; + break; + default: + alert_mask |= evt; + } + return _enable_alert(dev, alert_mask); +} + +/***************************************************************************** + *****************************************************************************/ +ePostCode ina226_probe(INA226_Dev *dev, POSTData *postData) +{ + uint16_t devId = 0x00; + uint16_t manfId = 0x0000; + if (ina226_getDevId(dev, &devId) != RETURN_OK) { + return POST_DEV_MISSING; + } + if (devId != INA226_DEVICE_ID) { + return POST_DEV_ID_MISMATCH; + } + + if (ina226_getMfgId(dev, &manfId) != RETURN_OK) { + return POST_DEV_MISSING; + } + if (manfId != INA226_MANFACTURE_ID) { + return POST_DEV_ID_MISMATCH; + } + post_update_POSTData(postData, dev->cfg.dev.bus, dev->cfg.dev.slave_addr,manfId, devId); + return POST_DEV_FOUND; +} diff --git a/firmware/psu/src/devices/ltc4015.c b/firmware/psu/src/devices/ltc4015.c new file mode 100644 index 0000000000..59afbcdd56 --- /dev/null +++ b/firmware/psu/src/devices/ltc4015.c @@ -0,0 +1,604 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** +#include "devices/i2c/threaded_int.h" +#include "inc/common/byteorder.h" +#include "inc/devices/ltc4015.h" +#include "helpers/math.h" +#include "helpers/memory.h" +#include "ltc4015_registers.h" + +#include +#include /* For abort() */ + +#define WTF abort() + +static ReturnStatus LTC4015_reg_write(const LTC4015_Dev *dev, + uint8_t regAddress, + uint16_t regValue) +{ + ReturnStatus status = RETURN_NOTOK; + I2C_Handle battHandle = i2c_get_handle(dev->cfg.i2c_dev.bus); + if (!battHandle) { + LOGGER_ERROR("LTC4015:ERROR:: Failed to open I2C bus for battery " + "charge controller 0x%x.\n", dev->cfg.i2c_dev.slave_addr); + } else { + regValue = htole16(regValue); + status = i2c_reg_write(battHandle, dev->cfg.i2c_dev.slave_addr, + regAddress, regValue, 2); + } + return status; +} + +static ReturnStatus LTC4015_reg_read(const LTC4015_Dev *dev, + uint8_t regAddress, + uint16_t *regValue) +{ + ReturnStatus status = RETURN_NOTOK; + I2C_Handle battHandle = i2c_get_handle(dev->cfg.i2c_dev.bus); + if (!battHandle) { + LOGGER_ERROR("LTC4015:ERROR:: Failed to open I2C bus for battery " + "charge controller 0x%x.\n", dev->cfg.i2c_dev.slave_addr); + } else { + status = i2c_reg_read(battHandle, dev->cfg.i2c_dev.slave_addr, + regAddress, regValue, 2); + *regValue = letoh16(*regValue); + } + return status; +} + +ReturnStatus LTC4015_cfg_icharge(LTC4015_Dev *dev, + uint16_t max_chargeCurrent) // milliAmps +{ + /* Maximum charge current target = (ICHARGE_TARGET + 1) * 1mV/RSNSB + => ICHARGE_TARGET = (target*RSNSB/1mV)-1 */ + int icharge_target = round((max_chargeCurrent * dev->cfg.r_snsb) / 1000.0) + - 1; + icharge_target = MAX(0, icharge_target); + return LTC4015_reg_write(dev, LTC4015_ICHARGE_TARGET_SUBADDR, + icharge_target); +} + +ReturnStatus LTC4015_get_cfg_icharge(LTC4015_Dev *dev, + uint16_t *max_chargeCurrent) // milliAmps +{ + /* Maximum charge current target = (ICHARGE_TARGET + 1) * 1mV/RSNSB */ + uint16_t ichargeCurrent = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, + LTC4015_ICHARGE_TARGET_SUBADDR, + &ichargeCurrent); + *max_chargeCurrent = (ichargeCurrent + 1) * 1000 / dev->cfg.r_snsb; + return status; +} + +ReturnStatus LTC4015_cfg_vcharge(LTC4015_Dev *dev, + uint16_t charge_voltageLevel) // millivolts +{ + /* See datasheet, page 61:VCHARGE_SETTING */ + const double target_v = charge_voltageLevel / (1000.0 * dev->cfg.cellcount); + double vchargeSetting; + switch (dev->cfg.chem) { + case LTC4015_CHEM_LEAD_ACID: + vchargeSetting = round((target_v - 2.0) * 105.0); + break; + case LTC4015_CHEM_LI_FE_PO4: + vchargeSetting = round((target_v - 3.4125) * 80.0); + break; + case LTC4015_CHEM_LI_ION: + vchargeSetting = round((target_v - 3.8125) * 80.0); + break; + default: + WTF; + break; + } + vchargeSetting = MAX(0, vchargeSetting); + return LTC4015_reg_write(dev, LTC4015_VCHARGE_SETTING_SUBADDR, + vchargeSetting); +} + +ReturnStatus LTC4015_get_cfg_vcharge(LTC4015_Dev *dev, + uint16_t *charge_voltageLevel) // millivolts +{ + /* See datasheet, page 61:VCHARGE_SETTING */ + uint16_t vchargeSetting = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, + LTC4015_VCHARGE_SETTING_SUBADDR, + &vchargeSetting); + switch (dev->cfg.chem) { + case LTC4015_CHEM_LEAD_ACID: + *charge_voltageLevel = + round(((vchargeSetting / 105.0) + 2.0) * + dev->cfg.cellcount * 1000.0); + break; + case LTC4015_CHEM_LI_FE_PO4: + *charge_voltageLevel = + round(((vchargeSetting / 80.0) + 3.4125) * + dev->cfg.cellcount * 1000.0); + break; + case LTC4015_CHEM_LI_ION: + *charge_voltageLevel = + round(((vchargeSetting / 80.0) + 3.8125) * + dev->cfg.cellcount * 1000.0); + break; + default: + WTF; + break; + } + /* TODO: bounds check? */ + + return status; +} + +/* Convert a voltage to a valid vbat register value */ +static uint16_t voltage_to_vbat_reg(LTC4015_Dev *dev, + int16_t voltage) +{ + switch (dev->cfg.chem) { + case LTC4015_CHEM_LEAD_ACID: + return (voltage / (dev->cfg.cellcount * 128.176)) * 1000.0; + case LTC4015_CHEM_LI_FE_PO4: + case LTC4015_CHEM_LI_ION: + return (voltage / (dev->cfg.cellcount * 192.264)) * 1000.0; + default: + WTF; + break; + } + return 0; /* Should never get here, but keeps compiler happy */ +} + +ReturnStatus LTC4015_cfg_battery_voltage_low(LTC4015_Dev *dev, + int16_t underVoltage) //millivolts +{ + /* See datasheet, page 56:VBAT_LO_ALERT_LIMIT + under voltage limit = [VBAT_*_ALERT_LIMIT] • x(uV) */ + return LTC4015_reg_write(dev, LTC4015_VBAT_LO_ALERT_LIMIT_SUBADDR, + voltage_to_vbat_reg(dev, underVoltage)); +} + +/* Convert a voltage to a valid vbat register value */ +static int16_t vbat_reg_to_voltage(LTC4015_Dev *dev, + uint16_t vbat_reg) +{ + switch (dev->cfg.chem) { + case LTC4015_CHEM_LEAD_ACID: + return ((int16_t) vbat_reg / 1000.0) * (128.176 * dev->cfg.cellcount); + case LTC4015_CHEM_LI_FE_PO4: + case LTC4015_CHEM_LI_ION: + return ((int16_t) vbat_reg / 1000.0) * (192.264 * dev->cfg.cellcount); + default: + WTF; + break; + } + return 0; /* Should never get here, but keeps compiler happy */ +} + +ReturnStatus LTC4015_get_cfg_battery_voltage_low(LTC4015_Dev *dev, + int16_t *underVolatage) //millivolts +{ + /* See datasheet, page 56 */ + uint16_t vbatLoLimit = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, + LTC4015_VBAT_LO_ALERT_LIMIT_SUBADDR, + &vbatLoLimit); + *underVolatage = vbat_reg_to_voltage(dev, vbatLoLimit); + return status; +} + +ReturnStatus LTC4015_cfg_battery_voltage_high(LTC4015_Dev *dev, + int16_t overVoltage) //millivolts +{ + /* See datasheet, page 56:VBAT_HI_ALERT_LIMIT + under voltage limit = [VBAT_*_ALERT_LIMIT] • x(uV) */ + return LTC4015_reg_write(dev, LTC4015_VBAT_HI_ALERT_LIMIT_SUBADDR, + voltage_to_vbat_reg(dev, overVoltage)); +} + +ReturnStatus LTC4015_get_cfg_battery_voltage_high(LTC4015_Dev *dev, + int16_t *overVoltage) //millivolts +{ + /* See datasheet, page 56 */ + uint16_t vbatHiLimit = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, + LTC4015_VBAT_HI_ALERT_LIMIT_SUBADDR, + &vbatHiLimit); + *overVoltage = vbat_reg_to_voltage(dev, vbatHiLimit); + return status; +} + + +ReturnStatus LTC4015_cfg_input_voltage_low(LTC4015_Dev *dev, + int16_t inputUnderVoltage) // millivolts +{ + /* See datasheet, page 56:VIN_LO_ALERT_LIMIT + VIN_LO_ALERT_LIMIT = limit/1.648mV */ + uint16_t vinLoLimit = (inputUnderVoltage / (1.648)); + return LTC4015_reg_write(dev, LTC4015_VIN_LO_ALERT_LIMIT_SUBADDR, + vinLoLimit); +} + +ReturnStatus LTC4015_get_cfg_input_voltage_low(LTC4015_Dev *dev, + int16_t *inpUnderVoltage) //millivolts +{ + /* See datasheet, page 56 + * VIN_LO_ALERT_LIMIT = (inpUnderVoltage/(1.648)) */ + uint16_t vInLoAlertLimit = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, + LTC4015_VIN_LO_ALERT_LIMIT_SUBADDR, + &vInLoAlertLimit); + *inpUnderVoltage = (int16_t) vInLoAlertLimit * 1.648; + return status; +} + +ReturnStatus LTC4015_cfg_input_current_high(LTC4015_Dev *dev, + int16_t inputOvercurrent) // milliAmps +{ + /* See datasheet, page 56:IIN_HI_ALERT_LIMIT + IIN_HI_ALERT_LIMIT = (limit*RSNSI)/1.46487uV */ + uint16_t iInHiLimit = ((inputOvercurrent * dev->cfg.r_snsi) / 1.46487); + return LTC4015_reg_write(dev, LTC4015_IIN_HI_ALERT_LIMIT_SUBADDR, + iInHiLimit); +} + +ReturnStatus LTC4015_get_cfg_input_current_high(LTC4015_Dev *dev, + int16_t *inpOverCurrent) +{ + /* See datasheet, page 56 + * IIN_HI_ALERT_LIMIT = ((inpOverCurrent*PWR_INT_BATT_RSNSI)/(1.46487)) */ + uint16_t iInHiALertLimit = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, + LTC4015_IIN_HI_ALERT_LIMIT_SUBADDR, + &iInHiALertLimit); + *inpOverCurrent = ((int16_t) iInHiALertLimit * 1.46487) / dev->cfg.r_snsi; + return status; +} + +ReturnStatus LTC4015_cfg_battery_current_low(LTC4015_Dev *dev, + int16_t lowbattCurrent) +{ + /* See datasheet, page 56:IBAT_LO_ALERT_LIMIT + IBAT_LO_ALERT_LIMIT = (limit*RSNSB)/1.46487uV */ + uint16_t iBatLoAlertLimit = (lowbattCurrent * dev->cfg.r_snsb) / (1.46487); + return LTC4015_reg_write(dev, LTC4015_IBAT_LO_ALERT_LIMIT_SUBADDR, + iBatLoAlertLimit); +} + +ReturnStatus LTC4015_get_cfg_battery_current_low(LTC4015_Dev *dev, + int16_t *lowbattCurrent) +{ + /* See datasheet, page 56 + * IBAT_LO_ALERT_LIMIT = ((current*PWR_INT_BATT_RSNSB)/(1.46487)) */ + uint16_t iBatLoAlertLimit = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, + LTC4015_IBAT_LO_ALERT_LIMIT_SUBADDR, + &iBatLoAlertLimit); + *lowbattCurrent = ((int16_t) iBatLoAlertLimit * 1.46487) / dev->cfg.r_snsb; + return status; +} + +ReturnStatus LTC4015_cfg_die_temperature_high(LTC4015_Dev *dev, + int16_t dieTemp) // Degrees C +{ + /* See datasheet, page 57:DIE_TEMP_HI_ALERT_LIMIT + DIE_TEMP_HI_ALERT_LIMIT = (DIE_TEMP • 12010)/45.6°C */ + uint16_t dieTempAlertLimit = (dieTemp * 45.6) + 12010; + return LTC4015_reg_write(dev, LTC4015_DIE_TEMP_HI_ALERT_LIMIT_SUBADDR, + dieTempAlertLimit); +} + +ReturnStatus LTC4015_get_cfg_die_temperature_high(LTC4015_Dev *dev, + int16_t *dieTemp) // Degrees C +{ + /* See datasheet, page 57 + * DIE_TEMP_HI_ALERT_LIMIT = (dieTemp • 12010)/45.6°C */ + uint16_t dieTempAlertLimit = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, + LTC4015_DIE_TEMP_HI_ALERT_LIMIT_SUBADDR, + &dieTempAlertLimit); + *dieTemp = (((int16_t) dieTempAlertLimit - 12010) / 45.6); + return status; +} + +ReturnStatus LTC4015_cfg_input_current_limit(LTC4015_Dev *dev, + uint16_t inputCurrentLimit) // milliAmps +{ + /* See datasheet, page 61:IIN_LIMIT_SETTING + IIN_LIMIT_SETTING = (limit * RSNSI / 500uV) - 1 */ + /* TODO: range check? this is only a 6-bit register */ + uint16_t iInLimitSetting = ((inputCurrentLimit * dev->cfg.r_snsi) / 500) - 1; + return LTC4015_reg_write(dev, LTC4015_IIN_LIMIT_SETTING_SUBADDR, + iInLimitSetting); +} + +ReturnStatus LTC4015_get_cfg_input_current_limit(LTC4015_Dev *dev, + uint16_t *currentLimit) //milli Amps +{ + /* See datasheet, page 56 + * Input current limit setting = (IIN_LIMIT_SETTING + 1) • 500uV / RSNSI */ + uint16_t iInlimitSetting = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, + LTC4015_IIN_LIMIT_SETTING_SUBADDR, + &iInlimitSetting); + *currentLimit = ((iInlimitSetting + 1) * 500.0) / dev->cfg.r_snsi; + return status; +} + +ReturnStatus LTC4015_get_die_temperature(LTC4015_Dev *dev, + int16_t *dieTemp) // Degrees C +{ + /* Datasheet page 71: temperature = (DIE_TEMP • 12010)/45.6°C */ + uint16_t dieTemperature = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, LTC4015_DIE_TEMP_SUBADDR, + &dieTemperature); + *dieTemp = (((int16_t) dieTemperature - 12010) / 45.6); + return status; +} + +ReturnStatus LTC4015_get_battery_current(LTC4015_Dev *dev, + int16_t *iBatt) //milliAmps +{ + /* Page 70: Battery current = [IBAT] * 1.46487uV/Rsnsb */ + uint16_t batteryCurrent = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, LTC4015_IBAT_SUBADDR, + &batteryCurrent); + *iBatt = ((float) ((int16_t) batteryCurrent * 1.46487)) / (dev->cfg.r_snsb); + return status; +} + +ReturnStatus LTC4015_get_input_current(LTC4015_Dev *dev, + int16_t *iIn) //milliAmps +{ + /* Page 71: Input current = [IIN] • 1.46487uV/Rsnsi */ + uint16_t inputCurrent = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, LTC4015_IIN_SUBADDR, + &inputCurrent); + *iIn = ((float) ((int16_t) inputCurrent * 1.46487)) / (dev->cfg.r_snsi); + return status; +} + +ReturnStatus LTC4015_get_battery_voltage(LTC4015_Dev *dev, + int16_t *vbat) //milliVolts +{ + /* Page 71: 2's compliment VBATSENS/cellcount = [VBAT] • [x]uV */ + uint16_t batteryVoltage = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, LTC4015_VBAT_SUBADDR, + &batteryVoltage); + *vbat = vbat_reg_to_voltage(dev, batteryVoltage); + return status; +} + +ReturnStatus LTC4015_get_input_voltage(LTC4015_Dev *dev, + int16_t *vIn) //milliVolts +{ + /* Page 71: 2's compliment Input voltage = [VIN] • 1.648mV */ + uint16_t inputVoltage = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, LTC4015_VIN_SUBADDR, + &inputVoltage); + *vIn = (int16_t) inputVoltage * 1.648; + return status; +} + +ReturnStatus LTC4015_get_system_voltage(LTC4015_Dev *dev, + int16_t *vSys) //milliVolts +{ + /* Page 71: 2's compliment system voltage = [VSYS] • 1.648mV */ + uint16_t sysVoltage = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, LTC4015_VSYS_SUBADDR, + &sysVoltage); + *vSys = (int16_t) sysVoltage * 1.648; + return status; +} + +ReturnStatus LTC4015_get_icharge_dac(LTC4015_Dev *dev, + int16_t *icharge) //milliAmps +{ + /* Page 72: (ICHARGE_DAC + 1) • 1mV/RSNSB */ + uint16_t ichargeDAC = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, LTC4015_ICHARGE_DAC_SUBADDR, + &ichargeDAC); + *icharge = (int16_t)((ichargeDAC + 1) / dev->cfg.r_snsb); + return status; +} + +static ReturnStatus _enable_limit_alerts(LTC4015_Dev *dev, uint16_t alertConfig) +{ + return LTC4015_reg_write(dev, LTC4015_EN_LIMIT_ALERTS_SUBADDR, alertConfig); +} + +static ReturnStatus _read_enable_limit_alerts(LTC4015_Dev *dev, uint16_t* regValue) +{ + return LTC4015_reg_read(dev, LTC4015_EN_LIMIT_ALERTS_SUBADDR, regValue); +} + +static ReturnStatus _read_limit_alerts(LTC4015_Dev *dev, uint16_t* regValue) +{ + return LTC4015_reg_read(dev, LTC4015_LIMIT_ALERTS_SUBADDR, regValue); +} + +static ReturnStatus _enable_charger_state_alerts(LTC4015_Dev *dev, + uint16_t regValue) +{ + return LTC4015_reg_write(dev, LTC4015_EN_CHARGER_STATE_ALERTS_SUBADDR, + regValue); +} + +static ReturnStatus _read_enable_charger_state_alerts(LTC4015_Dev *dev, + uint16_t* regValue) +{ + return LTC4015_reg_read(dev, LTC4015_EN_CHARGER_STATE_ALERTS_SUBADDR, + regValue); +} + +static ReturnStatus _read_charger_state_alerts(LTC4015_Dev *dev, + uint16_t *regValue) +{ + return LTC4015_reg_read(dev, LTC4015_CHARGER_STATE_ALERTS_SUBADDR, + regValue); +} + +static ReturnStatus _read_system_status(LTC4015_Dev *dev, uint16_t *regValue) +{ + ReturnStatus status = LTC4015_reg_read(dev, LTC4015_SYSTEM_STATUS_SUBADDR, + regValue); + return status; +} + +ReturnStatus LTC4015_get_bat_presence(LTC4015_Dev *dev, bool *present) +{ + ReturnStatus status = RETURN_OK; + uint16_t value = 0; + status = _read_charger_state_alerts(dev, &value); + *present = !(value & LTC4015_EVT_BMFA); + return status; +} + +static void _ltc4015_isr(void *context) { + LTC4015_Dev *dev = context; + ReturnStatus status = RETURN_OK; + uint16_t alert_status = 0; + int16_t val = 0; + bool present; + + /* See if we have a callback assigned to handle alerts */ + if (!dev->obj.alert_cb) { + return; + } + + /* Check battery missing alarm now */ + status = LTC4015_get_bat_presence(dev, &present); + if (status != RETURN_OK) { + return; + } + + /*If battery is missing no need to check other limit alerts*/ + if (!present) { + dev->obj.alert_cb(LTC4015_EVT_BMFA, val, dev->obj.cb_context); + return; + } + + /* Read the alert status register to clear the alert bits if set) */ + if (_read_limit_alerts(dev, &alert_status) != RETURN_OK) { + LOGGER_DEBUG("LTC4015:ERROR:: INT limit alerts read failed\n"); + return; + } + + if (alert_status & LTC4015_EVT_BVL) { + status = LTC4015_get_battery_voltage(dev, &val); + dev->obj.alert_cb(LTC4015_EVT_BVL, val, dev->obj.cb_context); + } + if (alert_status & LTC4015_EVT_BVH) { + status = LTC4015_get_battery_voltage(dev, &val); + dev->obj.alert_cb(LTC4015_EVT_BVH, val, dev->obj.cb_context); + } + if (alert_status & LTC4015_EVT_IVL) { + status = LTC4015_get_input_voltage(dev, &val); + dev->obj.alert_cb(LTC4015_EVT_IVL, val, dev->obj.cb_context); + } + if (alert_status & LTC4015_EVT_ICH) { + status = LTC4015_get_input_current(dev, &val); + dev->obj.alert_cb(LTC4015_EVT_ICH, val, dev->obj.cb_context); + } + if (alert_status & LTC4015_EVT_BCL) { + status = LTC4015_get_battery_current(dev, &val); + dev->obj.alert_cb(LTC4015_EVT_BCL, val, dev->obj.cb_context); + } + if (alert_status & LTC4015_EVT_DTH) { + status = LTC4015_get_die_temperature(dev, &val); + dev->obj.alert_cb(LTC4015_EVT_DTH, val, dev->obj.cb_context); + } +} + +ReturnStatus LTC4015_init(LTC4015_Dev *dev) +{ + dev->obj = (LTC4015_Obj){}; + + /* TODO: Do the pre-configuration here if needed */ + + if (dev->cfg.pin_alert) { + const uint32_t pin_evt_cfg = OCGPIO_CFG_INPUT | OCGPIO_CFG_INT_FALLING; + if (OcGpio_configure(dev->cfg.pin_alert, pin_evt_cfg) < OCGPIO_SUCCESS) { + return RETURN_NOTOK; + } + + /* Use a threaded interrupt to handle IRQ */ + // ThreadedInt_Init(dev->cfg.pin_alert, _ltc4015_isr, (void *)dev); + } + return RETURN_OK; +} + +void LTC4015_setAlertHandler(LTC4015_Dev *dev, LTC4015_CallbackFn alert_cb, + void *cb_context) { + dev->obj.alert_cb = alert_cb; + dev->obj.cb_context = cb_context; +} + +ReturnStatus LTC4015_enableLimitAlerts(LTC4015_Dev *dev, uint16_t alert_mask) +{ + uint16_t alert_reg; + + /* Read the alert status register to clear the alert bits if set) */ + ReturnStatus res = _read_limit_alerts(dev, &alert_reg); + if (res != RETURN_OK) { + return res; + } + + /* Get the previously configured alerts */ + res = _read_enable_limit_alerts(dev, &alert_reg); + if (res != RETURN_OK) { + return res; + } + + alert_reg |= alert_mask; + + return _enable_limit_alerts(dev, alert_reg); +} + +ReturnStatus LTC4015_enableChargerStateAlerts(LTC4015_Dev *dev, + uint16_t alert_mask) +{ + uint16_t alert_reg; + + /* Read the alert status register to clear the alert bits if set) */ + ReturnStatus res = _read_charger_state_alerts(dev, &alert_reg); + if (res != RETURN_OK) { + return res; + } + + /* Get the previously configured alerts */ + res = _read_enable_charger_state_alerts(dev, &alert_reg); + if (res != RETURN_OK) { + return res; + } + + alert_reg |= alert_mask; + + return _enable_charger_state_alerts(dev, alert_reg); +} + +void LTC4015_configure(LTC4015_Dev *dev) +{ + // OcGpio_configure(&dev->cfg.pin_lt4015_i2c_sel, OCGPIO_CFG_OUTPUT); +} + +ePostCode LTC4015_probe(LTC4015_Dev *dev, POSTData *postData) +{ + uint16_t ltcStatusReg = 0; + /* TODO: Check reading bits from System regsiter is enough to conclude + * whether battery is connected or not */ + if (_read_system_status(dev, <cStatusReg) != RETURN_OK) { + return POST_DEV_MISSING; + } + if (!(ltcStatusReg & LTC4015_CHARGER_ENABLED)) { + return POST_DEV_MISSING; + } + post_update_POSTData(postData, dev->cfg.i2c_dev.bus, dev->cfg.i2c_dev.slave_addr,0xFF, 0xFF); + return POST_DEV_FOUND; +} diff --git a/firmware/psu/src/devices/ltc4015_registers.h b/firmware/psu/src/devices/ltc4015_registers.h new file mode 100644 index 0000000000..71e11efeb0 --- /dev/null +++ b/firmware/psu/src/devices/ltc4015_registers.h @@ -0,0 +1,400 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#ifndef CHARGECTRL_LTC4015_H_ +#define CHARGECTRL_LTC4015_H_ + +/***************************************************************************** + * REGISTER DEFINITIONS + *****************************************************************************/ +// Battery voltage low alert limit - BITS[15:0] 0x0000 +#define LTC4015_VBAT_LO_ALERT_LIMIT_SUBADDR 0x01 +// Battery voltage high alert limit - BITS[15:0] 0x0000 +#define LTC4015_VBAT_HI_ALERT_LIMIT_SUBADDR 0x02 +// Input voltage low alert limit - BITS[15:0] 0x0000 +#define LTC4015_VIN_LO_ALERT_LIMIT_SUBADDR 0x03 +// Input voltage high alert limit - BITS[15:0] 0x0000 +#define LTC4015_VIN_HI_ALERT_LIMIT_SUBADDR 0x04 +// Output voltage low alert limit - BITS[15:0] 0x0000 +#define LTC4015_VSYS_LO_ALERT_LIMIT_SUBADDR 0x05 +// Output voltage high alert limit - BITS[15:0] 0x0000 +#define LTC4015_VSYS_HI_ALERT_LIMIT_SUBADDR 0x06 +// Input current high alert limit - BITS[15:0] 0x0000 +#define LTC4015_IIN_HI_ALERT_LIMIT_SUBADDR 0x07 +// Charge current low alert limit - BITS[15:0] 0x0000 +#define LTC4015_IBAT_LO_ALERT_LIMIT_SUBADDR 0x08 +// Die temperature high alert limit, - BITS[15:0] 0x0000 +#define LTC4015_DIE_TEMP_HI_ALERT_LIMIT_SUBADDR 0x09 +// Battery series resistance high alert limit - BITS[15:0] 0x0000 +#define LTC4015_BSR_HI_ALERT_LIMIT_SUBADDR 0x0A +// Thermistor ratio high (cold battery) alert limit - BITS[15:0] 0x0000 +#define LTC4015_NTC_RATIO_HI_ALERT_LIMIT_SUBADDR 0x0B +// Thermistor ratio low (hot battery) alert limit - BITS[15:0] 0x0000 +#define LTC4015_NTC_RATIO_LO_ALERT_LIMIT_SUBADDR 0x0C + +/* Bit fields: + * + * 15 : Enable meas_sys_valid_alert + * 14 : N/A + * 13 : Enable coulomb counter value low alert + * 12 : Enable coulomb counter value high alert + * 11 : Enable battery undervoltage alert + * 10 : Enable battery overvoltage alert + * 9 : Enable input undervoltage alert + * 8 : Enable input overvoltage alert + * 7 : Enable output undervoltage alert + * 6 : Enable output overvoltage alert + * 5 : Enable input overcurrent alert + * 4 : Enable battery current low alert + * 3 : Enable die temperature high alert + * 2 : Enable battery series resistance high alert + * 1 : Enable thermistor ratio high (cold battery) alert + * 0 : Enable thermistor ratio low (hot battery) alert + */ +// Enable limit monitoring and alert notification via SMBALERT - BITS[15:0] 0x0000 +#define LTC4015_EN_LIMIT_ALERTS_SUBADDR 0x0D + +/* Bit fields: + * + * 15:11 : N/A + * 10 : Enable alert for lead-acid equalize charge state + * 9 : Enable alert for absorb charge state + * 8 : Enable alert for charger suspended state + * 7 : Enable alert for precondition charge state + * 6 : Enable alert for constant current constant voltage state + * 5 : Enable alert for thermistor pause state + * 4 : Enable alert for timer termination state + * 3 : Enable alert for C/x termination state + * 2 : Enable max_charge_time_fault alert + * 1 : Enable alert for missing battery fault state + * 0 : Enable alert for shorted battery fault state + */ +// Enable charger state alert notification via SMBALERT - BITS[15:0] 0x0000 +#define LTC4015_EN_CHARGER_STATE_ALERTS_SUBADDR 0x0E + +/* Bit fields: + * + * 15:4 : N/A + * 3 : Enable alert for input undervoltage current limit active + * 2 : Enable alert for input current limit active + * 1 : Enable alert for constant current status + * 0 : Enable alert for constant voltage status + */ +// Enable charge status alert notification via SMBALERT - BITS[15:0] 0x0000 +#define LTC4015_EN_CHARGE_STATUS_ALERTS_SUBADDR 0x0F + +// Coulomb counter QCOUNT low alert limit, same format as QCOUNT (0x13) - BITS[15:0] : 0x0000 +#define LTC4015_QCOUNT_LO_ALERT_LIMIT_SUBADDR 0x10 +// Coulomb counter QCOUNT high alert limit, same format as QCOUNT (0x13) - BITS[15:0] : 0x0000 +#define LTC4015_QCOUNT_HI_ALERT_LIMIT_SUBADDR 0x11 +// Coulomb counter prescale factor - BITS[15:0] : 0x0200 +#define LTC4015_QCOUNT_PRESCALE_FACTOR_SUBADDR 0x12 +// Coulomb counter value - BITS[15:0] : 0x8000 +#define LTC4015_QCOUNT_SUBADDR 0x13 + +/* Bit fields: + * + * 15:9 : N/A + * 8 : Suspend battery charger operation + * 7:6 : N/A + * 5 : Perform a battery series resistance measurement + * 4 : Force measurement system to operate + * 3 : Enable Maximum Power Point Tracking + * 2 : Enable coulomb counter + * 1:0 : N/A + */ +// Configuration Settings - BITS[15:0] : 0x0000 +#define LTC4015_CONFIG_BITS_SUBADDR 0x14 + +// Input current limit setting = (IIN_LIMIT_SETTING + 1) • 500uV / RSNSI - BITS[5:0] : 0x3F +#define LTC4015_IIN_LIMIT_SETTING_SUBADDR 0x15 +// UVCLFB input undervoltage limit = (VIN_UVCL_SETTING + 1) • 4.6875mV - BITS[7:0] : 0xFF +#define LTC4015_VIN_UVCL_SETTING_SUBADDR 0x16 +#define LTC4015_RESERVED_0X17_SUBADDR 0x17 +#define LTC4015_RESERVED_0X18_SUBADDR 0x18 +// Write 0x534D to arm ship mode. Once armed, ship mode cannot be disarmed. +#define LTC4015_ARM_SHIP_MODE_SUBADDR 0x19 +// Maximum charge current target = (ICHARGE_TARGET + 1) • 1mV/RSNSB - BITS[4:0] +#define LTC4015_ICHARGE_TARGET_SUBADDR 0x1A +// Charge voltage target - BITS[5:0] +#define LTC4015_VCHARGE_SETTING_SUBADDR 0x1B +// Two’s complement Low IBAT threshold for C/x termination - BITS[15:0] +#define LTC4015_C_OVER_X_THRESHOLD_SUBADDR 0x1C +// Time in seconds with battery charger in the CV state before timer termination +// occurs (lithium chemistries only) +#define LTC4015_MAX_CV_TIME_SUBADDR 0x1D +// Time in seconds before a max_charge_time fault is declared. Set to zero to +// disable max_charge_time fault +#define LTC4015_MAX_CHARGE_TIME_SUBADDR 0x1E +// Value of NTC_RATIO for transition between JEITA regions 2 and 1 (off) - BITS[15:0] : 0x3F00 +#define LTC4015_JEITA_T1_SUBADDR 0x1F +// Value of NTC_RATIO for transition between JEITA regions 3 and 2 - BITS[15:0] : 0x372A +#define LTC4015_JEITA_T2_SUBADDR 0x20 +// Value of NTC_RATIO for transition between JEITA regions 4 and 3 - BITS[15:0] : 0x1F27 +#define LTC4015_JEITA_T3_SUBADDR 0x21 +// Value of NTC_RATIO for transition between JEITA regions 5 and 4 - BITS[15:0] : 0x1BCC +#define LTC4015_JEITA_T4_SUBADDR 0x22 +// Value of NTC_RATIO for transition between JEITA regions 6 and 5 - BITS[15:0] : 0x18B9 +#define LTC4015_JEITA_T5_SUBADDR 0x23 +// Value of NTC_RATIO for transition between JEITA regions 7 (off) and 6 - BITS[15:0] : 0x136D +#define LTC4015_JEITA_T6_SUBADDR 0x24 + +/* Bit Fields: + * + * 15:10 : N/A + * 9:5 : vcharge_jeita_6 + * 4:0 : vcharge_jeita_5 + */ +// VCHARGE values for JEITA temperature regions 6 and 5 +#define LTC4015_VCHARGE_JEITA_6_5_SUBADDR 0x25 + +/* Bit Fields: + * + * 15 : N/A + * 14:10 : vcharge_jeita_4 + * 9:5 : vcharge_jeita_3 + * 4:0 : vcharge_jeita_4 + */ +// VCHARGE values for JEITA temperature regions 4, 3, and 2 +#define LTC4015_VCHARGE_JEITA_4_3_2_SUBADDR 0x26 + +/* Bit Fields: + * + * 15:10 : N/A + * 9:5 : icharge_jeita_6 + * 4:0 : icharge_jeita_5 + */ +// ICHARGE_TARGET values for JEITA temperature regions 6 and 5 - BITS[15:0] : 0x01EF +#define LTC4015_ICHARGE_JEITA_6_5_SUBADDR 0x27 + +/* Bit Fields: + * + * 15 : N/A + * 14:10 : icharge_jeita_4 + * 9:5 : icharge_jeita_3 + * 4:0 : icharge_jeita_4 + */ +// ICHARGE_TARGET value for JEITA temperature regions 4, 3, and 2 - BITS[15:0] : 0x7FEF +#define LTC4015_ICHARGE_JEITA_4_3_2_SUBADDR 0x28 + +/* Bit Fields: + * + * 15:3 : N/A + * 2 : Enable C/x termination + * 1 : Enable lead acid charge voltage temperature compensation + * 0 : Enable JEITA temperature profile + */ +// Battery charger configuration settings +#define LTC4015_CHARGER_CONFIG_BITS_SUBADDR 0x29 + +// LiFePO4/lead-acid absorb voltage adder, bits 15:6 are reserved +#define LTC4015_VABSORB_DELTA_SUBADDR 0x2A +// Maximum time for LiFePO4/lead-acid absorb charge +#define LTC4015_MAX_ABSORB_TIME_SUBADDR 0x2B +// Lead-acid equalize charge voltage adder, bits 15:6 are reserved - BITS[15:0] : 0x002A +#define LTC4015_VEQUALIZE_DELTA_SUBADDR 0x2C +// Lead-acid equalization time - BITS[15:0] : 0x0E10 +#define LTC4015_EQUALIZE_TIME_SUBADDR 0x2D +// LiFeP04 recharge threshold - BITS[15:0] : 0x4410 +#define LTC4015_LIFEPO4_RECHARGE_THRESHOLD_SUBADDR 0x2E +#define LTC4015_RESERVED_0X2F_SUBADDR 0x2F +// For lithium chemistries, indicates the time (in sec) that the battery has +// been charging +#define LTC4015_MAX_CHARGE_TIMER_SUBADDR 0x30 +// For lithium chemistries, indicates the time (in sec) that the battery has +// been in constant-voltage regulation +#define LTC4015_CV_TIMER_SUBADDR 0x31 +// For LiFePO4 and lead-acid batteries, indicates the time (in sec) that the +// battery has been in absorb phase +#define LTC4015_ABSORB_TIMER_SUBADDR 0x32 +// For lead-acid batteries, indicates the time (in sec) that the battery has +// been in EQUALIZE phase +#define LTC4015_EQUALIZE_TIMER_SUBADDR 0x33 + +/* Bit Fields: + * + * 15:3 : N/A + * 10 : Indicates battery charger is in lead-acid equalization charge state + * 9 : Indicates battery charger is in absorb charge state + * 8 : Indicates battery charger is in charger suspended state + * 7 : Indicates battery charger is in precondition charge state + * 6 : Indicates battery charger is in CC-CV state + * 5 : Indicates battery charger is in thermistor pause state + * 4 : Indicates battery charger is in timer termination state + * 3 : Indicates battery charger is in C/x termination state + * 2 : indicates battery charger is in max_charge_time_fault state + * 1 : Indicates battery charger is in missing battery fault state + * 0 : Indicates battery charger is in shorted battery fault state + */ +// Real time battery charger state indicator. Individual bits are mutually +// exclusive. Bits 15:11 are reserved. +#define LTC4015_CHARGER_STATE_SUBADDR 0x34 + +/* Bit Fields: + * + * 15:4 : N/A + * 3 : Indicates the input undervoltage control loop is actively + * controlling power delivery based on VIN_UVCL_SETTING + * 2 : Indicates the input current limit control loop is actively + * controlling power delivery based on IIN_LIMIT[_DAC][_SETTING] + * 1 : Indicates the charge current control loop is actively controlling + * power delivery based on ICHARGE_DAC + * 0 : Indicates the battery voltage control loop is actively controlling + * power delivery based on VCHARGE_DAC + */ +// Charge status indicator. Individual bits are mutually exclusive. Only active +// in charging states. +#define LTC4015_CHARGE_STATUS_SUBADDR 0x35 + +/* Bit Fields: + * + * 15 : Indicates that measurement system results have become valid. + * 14 : N/A + * 13 : Indicates QCOUNT has fallen below QCOUNT_LO_ALERT_LIMIT + * 12 : Indicates QCOUNT has exceeded QCOUNT_HI_ALERT_LIMIT + * 11 : Indicates VBAT has fallen below VBAT_LO_ALERT_LIMIT + * 10 : Indicates VBAT has exceeded VBAT_HI_ALERT_LIMIT + * 9 : Indicates VIN has fallen below VIN_LO_ALERT_LIMIT + * 8 : Indicates VIN has exceeded VIN_HI_ALERT_LIMIT + * 7 : Indicates VSYS has fallen below VSYS_LO_ALERT_LIMIT + * 6 : Indicates VSYS has exceeded VSYS_HI_ALERT_LIMIT + * 5 : Indicates IIN has exceeded IIN_HI_ALERT_LIMIT + * 4 : Indicates IBAT has fallen below IBAT_LO_ALERT_LIMIT + * 3 : Indicates DIE_TEMP has exceeded DIE_TEMP_HI_ALERT_LIMIT + * 2 : Indicates BSR has exceeded BSR_HI_ALERT_LIMIT + * 1 : Indicates NTC_RATIO has exceeded NTC_RATIO_HI_ALERT_LIMIT + * 0 : Indicates NTC_RATIO has fallen below NTC_RATIO_LO_ALERT_LIMIT + */ +// Limit alert register.Individual bits are enabled by EN_LIMIT_ALERTS (0x0D). +// Writing 0 to any bit clears that alert. Once set, alert bits remain high +// until cleared or disabled. +#define LTC4015_LIMIT_ALERTS_SUBADDR 0x36 + +/* Bit Fields: + * + * 15:11 : N/A + * 10 : Alert indicates charger has entered equalize charge state + * 9 : Alert indicates charger has entered absorb charge state + * 8 : Alert indicates charger has been suspended + * 7 : Alert indicates charger has entered preconditioning charge state + * 6 : Alert indicates charger has entered CC-CV charge state + * 5 : Alert indicates charger has entered thermistor pause state + * 4 : Alert indicates timer termination has occurred + * 3 : Alert indicates C/x termination has occurred + * 2 : Alert indicates charger has entered max_charge_time_fault state + * 1 : Alert indicates battery missing fault has occurred + * 0 : Alert indicates battery short fault has occurred + */ +// Charger state alert register. Individual bits are enabled by EN_CHARGER_STATE_ALERTS (0x0E). +#define LTC4015_CHARGER_STATE_ALERTS_SUBADDR 0x37 + +/* Bit Fields: + * + * 15:4 : N/A + * 3 : Alert indicates that vin_uvcl_active has occurred + * 2 : Alert indicates iin_limit_active has occurred + * 1 : Alert indicates constant_current has occurred + * 0 : Alert indicates constant_voltage has occurred + */ +// Alerts that CHARGE_STATUS indicators have occurred. +// Individual bits are enabled by EN_CHARGE_STATUS_ALERTS (0x0F) +#define LTC4015_CHARGE_STATUS_ALERTS_SUBADDR 0x38 + +/* Bit Fields: + * + * 15:14 : N/A + * 13 : Indicates that the battery charger is active + * 12 : N/A + * 11 : Indicates the MPPT pin is set to enable Maximum Power Point Tracking + * 10 : Indicates a rising edge has been detected at the EQ pin, and an + * equalize charge is queued + * 9 : Indicates DRVCC voltage is above switching regulator undervoltage + * lockout level (4.3V typ) + * 8 : Indicates an invalid combination of CELLS pin settings + * 7 : N/A + * 6 : Indicates all system conditions are met to allow battery charger + * operation. + * 5 : Indicates no resistor has been detected at the RT pin + * 4 : Indicates die temperature is greater than thermal shutdown level + * (160C typical) + * 3 : Indicates VIN voltage is greater than overvoltage lockout level + * (38.6V typical) + * 2 : Indicates VIN voltage is sufficiently greater than BATSENSE for + * switching regulator operation (200mV typical) + * 1 : Indicates INTVCC voltage is above switching regulator undervoltage + * lockout level (4.3V typ) + * 0 : Indicates INTVCC voltage is greater than measurement system lockout + * level (2.8V typical) + */ +// Real time system status indicator bits +#define LTC4015_SYSTEM_STATUS_SUBADDR 0x39 + +// Two’s complement ADC measurement result for the BATSENS pin. +// VBATSENS/cellcount = [VBAT] • 192.264uV for lithium chemistries. +// VBATSENS/cellcount = [VBAT] • 128.176uV for lead-acid. +#define LTC4015_VBAT_SUBADDR 0x3A + +// Two’s complement ADC measurement result for VIN. +// VVIN = [VIN] • 1.648mV +#define LTC4015_VIN_SUBADDR 0x3B + +// Two’s complement ADC measurement result for VSYS. +// VSYS = [VSYS] • 1.648mV +#define LTC4015_VSYS_SUBADDR 0x3C + +// Two’s complement ADC measurement result for (VCSP – VCSN). +// Charge current (into the battery) is represented as a positive number. +// Battery current = [IBAT] • 1.46487uV/RSNSB +#define LTC4015_IBAT_SUBADDR 0x3D + +// Two’s complement ADC measurement result for (VCLP – VCLN). +// Input current = [IIN] • 1.46487uV/RSNSI +#define LTC4015_IIN_SUBADDR 0x3E + +// Two’s complement ADC measurement result for die temperature. +// Temperature = (DIE_TEMP – 12010)/45.6°C +#define LTC4015_DIE_TEMP_SUBADDR 0x3F + +// Two’s complement ADC measurement result for NTC thermistor ratio. +// RNTC = NTC_RATIO • RNTCBIAS/(21845.0 – NTC_RATIO) +#define LTC4015_NTC_RATIO_SUBADDR 0x40 + +// Calculated battery series resistance. +// For lithium chemistries, series resistance/cellcount = BSR • RSNSB/500.0 +// For lead-acid chemistries, series resistance/cellcount = BSR • RSNSB/750.0 +#define LTC4015_BSR_SUBADDR 0x41 + +// JEITA temperature region of the NTC thermistor (Li Only). +// Active only when EN_JEITA=1 (Only Bits[2:0] used) +#define LTC4015_JEITA_REGION_SUBADDR 0x42 + +/* Bit Fields: + * + * 15:12 : N/A + * 11:8 : programmed battery chemistry + * 7:4 : Reserved + * 3:0 : Cell count as set by CELLS pins + */ +// Readout of CHEM and CELLS pin settings +#define LTC4015_CHEM_CELLS_SUBADDR 0x43 +// Charge current control DAC control bits (Only Bits[4:0] used) +#define LTC4015_ICHARGE_DAC_SUBADDR 0x44 +// Charge voltage control DAC control bits (Only Bits[5:0] used) +#define LTC4015_VCHARGE_DAC_SUBADDR 0x45 +// Input current limit control DAC control word (Only Bits[5:0] used) +#define LTC4015_IIN_LIMIT_DAC_SUBADDR 0x46 +// Digitally filtered two’s complement ADC measurement result for battery voltage +#define LTC4015_VBAT_FILT_SUBADDR 0x47 +// This 16-bit two's complement word is the value of IBAT (0x3D) used in calculating BSR. +#define LTC4015_ICHARGE_BSR_SUBADDR 0x48 +#define LTC4015_RESERVED_0X49_SUBADDR 0x49 +// Measurement valid bit, bit 0 is a 1 when the telemetry(ADC) system is ready +#define LTC4015_MEAS_SYS_VALID_SUBADDR 0x4A + +#endif /* CHARGECTRL_LTC4015_H_ */ diff --git a/firmware/psu/src/devices/ltc4274.c b/firmware/psu/src/devices/ltc4274.c new file mode 100644 index 0000000000..981270e94d --- /dev/null +++ b/firmware/psu/src/devices/ltc4274.c @@ -0,0 +1,1075 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** +#include "Board.h" +#include "inc/common/global_header.h" +#include "inc/common/i2cbus.h" +#include "inc/devices/ltc4274.h" +#include "inc/subsystem/power/power.h" + +#include +#include + +/***************************************************************************** + * MACRO DEFINITIONS + *****************************************************************************/ + + +/* Register map */ +#define LTC4274_REG_INTERRUPT_STATUS 0x00 +#define LTC4274_REG_INTERRUPT_MASK 0x01 +#define LTC4274_REG_POWER_EVENT 0x02 +#define LTC4274_REG_POWER_EVENT_COR 0x03 +#define LTC4274_REG_DETECT_EVENT 0x04 +#define LTC4274_REG_DETECT_EVENT_COR 0x05 +#define LTC4274_REG_FAULT_EVENT 0x06 +#define LTC4274_REG_FAULT_EVENT_COR 0x07 +#define LTC4274_REG_START_EVENT 0x08 +#define LTC4274_REG_START_EVENT_COR 0x09 +#define LTC4274_REG_SUPPLY_EVENT 0x0A +#define LTC4274_REG_SUPPLY_EVENT_COR 0x0B +#define LTC4274_REG_STATUS 0x0C +#define LTC4274_REG_POWER_STATUS 0x10 +#define LTC4274_REG_PNI_STATUS 0x11 +#define LTC4274_REG_OPERATION_MODE 0x12 +#define LTC4274_REG_ENABLE_DUSCONNECT SENSING 0X13 +#define LTC4274_REG_DETECT_CLASS_ENABLE 0x14 +#define LTC4274_REG_MIDSPAN 0x15 +#define LTC4274_REG_MCONF 0x17 +#define LTC4274_REG_DETPB 0x18 +#define LTC4274_REG_PWRPB 0x19 +#define LTC4274_REG_RSTPB 0x1A +#define LTC4274_REG_ID 0x1B +#define LTC4274_REG_TLIMIT 0x1E +#define LTC4274_REG_IP1LSB 0x30 +#define LTC4274_REG_IP1MSB 0x31 +#define LTC4274_REG_VP1LSB 0x32 +#define LTC4274_REG_VP1MSB 0x33 +#define LTC4274_REG_FIRMWARE 0x41 +#define LTC4274_REG_WDOG 0x42 +#define LTC4274_REG_DEVID 0x43 +#define LTC4274_REG_HP_ENABLE 0x44 +#define LTC4274_REG_HP_MODE 0x46 +#define LTC4274_REG_CUT1 0x47 +#define LTC4274_REG_LIM1 0x48 +#define LTC4274_REG_IHP_STATUS 0x49 + +/* Interrupt bits */ +#define LTC4274_SUPPLY_INTERRUPT 0x80 +#define LTC4274_TSTART_INTERRUPT 0x40 +#define LTC4274_TCUT_INTERRUPT 0x20 +#define LTC4274_CLASSIFICATION_INTERRUPT 0x10 +#define LTC4274_DETECT_INTERRUPT 0x08 +#define LTC4274_DISCONNECT_INTERRUPT 0x04 +#define LTC4274_POWERGOOD_INTERRUPT 0x02 +#define LTC4274_PWERENABLE_INTERRUPT 0x01 + +/* Events and status control bits*/ +#define LTC4274_PWR_GOOD_BIT 0x10 +#define LTC4274_PWR_STATE_CHANGE_BIT 0x01 +#define LTC4274_CLASSIFICATION_COMPLETE_BIT 0x10 +#define LTC4274_DETECT_COMPLETE_BIT 0X01 +#define LTC4274_DISCONNECT_TDIS_BIT 0x10 +#define LTC4274_OVERCURRENT_TCUT_BIT 0x01 +#define LTC4274_CURRENT_LIMIT_TOUT_BIT 0x10 +#define LTC4274_START_OCURRENT_TOUT_BIT 0X01 +#define LTC4274_PWR_GOOD_BIT 0x10 +#define LTC4274_PWR_ENABLE_BIT 0x01 +#define LTC4274_LTPOE_BIT 0x80 +#define LTC4274_CLASS_BIT 0x70 +#define LTC4274_DETECT_BIT 0X07 +#define LTC4274_ENABLE_CLASSIFICATION_BIT 0X10 +#define LTC4274_ENABLE_DETECTION_BIT 0x01 +#define LTC4274_SUPPLY_EVENT_FAILURE 0xF0 + +/* PSE Configuration */ +#define LTC4274_INTERRUPT_SET 0x1E +#define LTC4274_OPERATING_MODE_SET 0x03 +#define LTC4274_DETCET_CLASS_ENABLE 0x11 +#define LTC4274_MISC_CONF 0xD1 + +/* PSE operating modes */ +#define LTC4274_SHUTDOWN_MODE 0x00 +#define LTC4274_MANUAL_MODE 0x01 +#define LTC4274_SEMIAUTO_MODE 0x02 +#define LTC4274_AUTO_MODE 0x03 + +#define LTC4274_INTERRUPT_ENABLE 0x80 +#define LTC4274_DETECT_ENABLE 0x40 +#define LTC4274_FAST_IV 0x20 +#define LTC4274_MSD_MASK 0x01 + +#define LTC4274_HP_ENABLE 0x11 + +typedef struct { + ePSEDetection detectStatus; + ePSEClassType classStatus; + ePSEPowerState powerGoodStatus; +} tPower_PSEStatus_Data; + +typedef struct { + tPower_PSEStatus_Data pseStatus; + ePSEAlert psealert; +} tPower_PSEStatus_Info; + +static tPower_PSEStatus_Info PSEStatus_Info; + +/****************************************************************************** + * @fn ltc4274_raw_write + * + * @brief Write to PSE register + * + * @args I2C device, Slave address, register address and value to be written. + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_write(const I2C_Dev *i2c_dev, + uint8_t regAddress, + uint8_t regValue) +{ + ReturnStatus status = RETURN_NOTOK; + I2C_Handle pseHandle = i2c_get_handle(i2c_dev->bus); + if (!pseHandle) { + LOGGER_ERROR("LTC4274:ERROR:: Failed to get I2C Bus for PSE" + "0x%x on bus 0x%x.\n", i2c_dev->slave_addr, i2c_dev->bus); + } else { + status = i2c_reg_write(pseHandle,i2c_dev->slave_addr, regAddress, + regValue, 1); + } + return status; +} + +/****************************************************************************** + * @fn ltc4274_read + * + * @brief read two bytesfrom PSE register. + * + * @args I2C device, Slave address, register address and value read. + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_read(const I2C_Dev *i2c_dev, + uint8_t regAddress, + uint8_t *regValue) +{ + ReturnStatus status = RETURN_NOTOK; + I2C_Handle pseHandle = i2c_get_handle(i2c_dev->bus); + if (!pseHandle) { + LOGGER_ERROR("LTC4274:ERROR:: Failed to get I2C Bus for PSE" + "0x%x on bus 0x%x.\n", i2c_dev->slave_addr, i2c_dev->bus); + } else { + /* TODO: refactor i2c_reg_read to not require uint16 */ + uint16_t value; + status = i2c_reg_read(pseHandle, i2c_dev->slave_addr, regAddress, + &value, 1); + *regValue = (uint8_t)value; + } + return status; +} + + +/****************************************************************************** + * @fn ltc4274_set_cfg_operation_mode + * + * @brief Configure PSE operational mode. + * + * @args I2c device struct, Address and operatingMode + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_set_cfg_operation_mode(const I2C_Dev *i2c_dev, uint8_t operatingMode) +{ + ReturnStatus status = RETURN_OK; + status = ltc4274_write( i2c_dev, LTC4274_REG_OPERATION_MODE, operatingMode); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: Write failed to the Operation mode register of PSE.\n"); + } + return status; +} + +/****************************************************************************** + * @fn ltc4274_get_operation_mode + * + * @brief read PSE operational mode. + * + * @args I2c device struct, Address and operatingMode + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_get_operation_mode(const I2C_Dev *i2c_dev, uint8_t *operatingMode) +{ + ReturnStatus status = RETURN_OK; + status = ltc4274_read( i2c_dev, LTC4274_REG_OPERATION_MODE, operatingMode); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: Read failed from the Operation mode register of PSE.\n"); + } + return status; +} + +/****************************************************************************** + * @fn ltc4274_set_cfg_detect_enable + * + * @brief Configure PSE detect and classification enable. + * + * @args I2c device struct, Address and detectEnable + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_set_cfg_detect_enable(const I2C_Dev *i2c_dev, uint8_t detectEnable) +{ + ReturnStatus status = RETURN_OK; + + //Enable detect and classfication of PD + status = ltc4274_write( i2c_dev, LTC4274_REG_DETECT_CLASS_ENABLE, + detectEnable); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE detect enable setting failed.\n"); + } + return status; +} + +/****************************************************************************** + * @fn ltc4274_get_detect_enable + * + * @brief Read PSE detect and classification enable configuration. + * + * @args I2c device struct, Address and detectEnable + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_get_detect_enable(const I2C_Dev *i2c_dev, uint8_t *detectVal) +{ + ReturnStatus status = RETURN_OK; + //Enable detect and classfication of PD + uint8_t val = 0; + status = ltc4274_read( i2c_dev, LTC4274_REG_DETECT_CLASS_ENABLE, &val); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE detect enable config read failed.\n"); + } + *detectVal = (val & 0x07); + return status; +} + +/****************************************************************************** + * @fn ltc4274_set_interrupt_mask + * + * @brief Configure Interrupts for PSE device. + * + * @args I2c device struct,Address and intrMask + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_set_interrupt_mask(const I2C_Dev *i2c_dev, uint8_t interruptMask) +{ + ReturnStatus status = RETURN_OK; + /* Enable interrupts for power event,power good, supply related faults or + * over currrent*/ + status = ltc4274_write( i2c_dev, LTC4274_REG_INTERRUPT_MASK, interruptMask); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE interrupt mask setting failed.\n"); + } + return status; +} + +/****************************************************************************** + * @fn ltc4274_get_interrupt_mask + * + * @brief Read Interrupts for PSE device configuration. + * + * @args I2c device struct, Address and intrMask + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_get_interrupt_mask(const I2C_Dev *i2c_dev, uint8_t *intrMask) +{ + ReturnStatus status = RETURN_OK; + /* Enable interrupts for power event,power good, supply related faults or + * over currrent*/ + status = ltc4274_read( i2c_dev, LTC4274_REG_INTERRUPT_MASK, intrMask); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE interrupt mask config read failed.\n"); + } + return status; +} + +/****************************************************************************** + * @fn ltc4274_cfg_interrupt_enable + * + * @brief Configure Interrupts for PSE device. + * + * @args I2c device struct, Address and interruptBit + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_cfg_interrupt_enable(const I2C_Dev *i2c_dev, bool enable) +{ + ReturnStatus status = RETURN_OK; + uint8_t interruptEnable = 0x00; + if(enable) { + interruptEnable = LTC4274_INTERRUPT_ENABLE; + } + /*Enable interrupts from Misc register*/ + status = ltc4274_write( i2c_dev, LTC4274_REG_MCONF, interruptEnable); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE interrupt enable failed.\n"); + } + return status; +} + +/****************************************************************************** + * @fn ltc4274_get_interrupt_enable + * + * @brief Reads Interrupts enable config for PSE device. + * + * @args I2c device struct, Address and interruptBit + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_get_interrupt_enable(const I2C_Dev *i2c_dev, uint8_t *interruptEnable) +{ + ReturnStatus status = RETURN_OK; + /*Enable interrupts from Misc register*/ + status = ltc4274_read( i2c_dev, LTC4274_REG_MCONF, interruptEnable); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE interrupt enable config read failed.\n"); + } + return status; +} + +/****************************************************************************** + * @fn ltc4274_set_cfg_pshp_feature + * + * @brief Configure high power feature for LTEPOE++. + * + * @args I2c device struct ,Address and hpEnable + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_set_cfg_pshp_feature(const I2C_Dev *i2c_dev, uint8_t hpEnable) +{ + ReturnStatus status = RETURN_OK; + /*Enbale high power feature */ + status = ltc4274_write( i2c_dev, LTC4274_REG_HP_ENABLE, hpEnable); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE high power mode setting failed.\n"); + } + return status; +} + +/****************************************************************************** + * @fn ltc4274_get_pshp_feature + * + * @brief read high power feature config for LTEPOE++. + * + * @args I2c device struct, Address and hpEnable + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_get_pshp_feature(const I2C_Dev *i2c_dev, uint8_t *hpEnable) +{ + ReturnStatus status = RETURN_OK; + /*Enbale high power feature */ + status = ltc4274_read( i2c_dev, LTC4274_REG_HP_ENABLE, hpEnable); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE high power mode config read failed.\n"); + } + return status; +} +/****************************************************************************** + * @fn _get_pse_class_enum + * + * @brief get PSE classs. + * + * @args value and pointer to class type + * + * @return None + */ +static void _get_pse_class_enum(uint8_t val , ePSEClassType *pseClass) +{ + switch (val) { + case LTC4274_CLASSTYPE_UNKOWN: + { + *pseClass = LTC4274_CLASSTYPE_UNKOWN; + } + break; + case LTC4274_CLASSTYPE_1: + { + *pseClass = LTC4274_CLASSTYPE_1; + } + break; + case LTC4274_CLASSTYPE_2: + { + *pseClass = LTC4274_CLASSTYPE_2; + } + break; + case LTC4274_CLASSTYPE_3: + { + *pseClass = LTC4274_CLASSTYPE_3; + } + break; + case LTC4274_CLASSTYPE_4: + { + *pseClass = LTC4274_CLASSTYPE_4; + } + break; + case LTC4274_CLASSTYPE_RESERVED: + { + *pseClass = LTC4274_CLASSTYPE_RESERVED; + } + break; + case LTC4274_CLASSTYPE_0: + { + *pseClass = LTC4274_CLASSTYPE_0; + } + break; + case LTC4274_OVERCURRENT: + { + *pseClass = LTC4274_OVERCURRENT; + } + break; + case LTC4274_LTEPOE_TYPE_52_7W: + { + *pseClass = LTC4274_LTEPOE_TYPE_52_7W; + } + break; + case LTC4274_LTEPOE_TYPE_70W: + { + *pseClass = LTC4274_LTEPOE_TYPE_70W; + } + break; + case LTC4274_LTEPOE_TYPE_90W: + { + *pseClass = LTC4274_LTEPOE_TYPE_90W; + } + break; + case LTC4274_LTEPOE_TYPE_38_7W: + { + *pseClass = LTC4274_LTEPOE_TYPE_38_7W; + } + break; + default: + { + *pseClass = LTC4274_LTEPOE_RESERVED; + } + } +} + + +/****************************************************************************** + * @fn _get_pse_detect_enum + * + * @brief get PSE detect. + * + * @args value and pointer to detection status + * + * @return None + */ +static void _get_pse_detect_enum(uint8_t val , ePSEDetection *pseDetect) +{ + switch (val) { + case LTC4274_DETECT_UNKOWN: + { + *pseDetect = LTC4274_DETECT_UNKOWN; + } + break; + case LTC4274_SHORT_CIRCUIT: + { + *pseDetect = LTC4274_SHORT_CIRCUIT; + } + break; + case LTC4274_CPD_HIGH: + { + *pseDetect = LTC4274_CPD_HIGH; + } + break; + case LTC4274_RSIG_LOW: + { + *pseDetect = LTC4274_RSIG_LOW; + } + break; + case LTC4274_SIGNATURE_GOOD: + { + *pseDetect = LTC4274_SIGNATURE_GOOD; + } + break; + case LTC4274_RSIG_TOO_HIGH: + { + *pseDetect = LTC4274_RSIG_TOO_HIGH; + } + case LTC4274_OPEN_CIRCUIT: + { + *pseDetect = LTC4274_OPEN_CIRCUIT; + } + break; + default: + { + *pseDetect = LTC4274_DETECT_ERROR; + } + } +} +/****************************************************************************** + * @fn ltc4274_get_detection_status + * + * @brief Read PSE class + * + * @args I2c device struct and pointer to class + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_get_detection_status(const I2C_Dev *i2c_dev, ePSEDetection *pseDetect) +{ + ReturnStatus status = RETURN_OK; + uint8_t val = 0; + status = ltc4274_read( i2c_dev, LTC4274_REG_DETECT_EVENT, &val); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE detection status read failed.\n"); + } + if (!(LTC4274_DETECTION_COMPLETE(val))) { + *pseDetect =LTC4274_DETECT_ERROR; + } else { + status = ltc4274_read( i2c_dev, LTC4274_REG_STATUS, &val); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE detection code read failed.\n"); + } + val = LTC4374_DETECT(val); + _get_pse_detect_enum(val,pseDetect); + } + + return status; +} + +/****************************************************************************** + * @fn ltc4274_get_class_status + * + * @brief Read PSE class + * + * @args I2c device struct and pointer to class + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_get_class_status(const I2C_Dev *i2c_dev, ePSEClassType *pseClass) +{ + ReturnStatus status = RETURN_OK; + uint8_t val = 0; + status = ltc4274_read( i2c_dev, LTC4274_REG_DETECT_EVENT, &val); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE class status read failed.\n"); + } + if (!(LTC4274_CLASSIFICATION_COMPLETE(val))) { + *pseClass = LTC4274_CLASS_ERROR; + } else { + status = ltc4274_read( i2c_dev, LTC4274_REG_STATUS, &val); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE class code read failed.\n"); + } + val = LTC4374_CLASS(val); + _get_pse_class_enum(val, pseClass); + } + return status; +} + +/****************************************************************************** + * @fn ltc4274_get_powergood_status + * + * @brief Read PSE power good + * + * @args I2c device struct, Address and detectEnable + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_get_powergood_status(const I2C_Dev *i2c_dev, uint8_t *psePwrGood) +{ + ReturnStatus status = RETURN_OK; + status = ltc4274_read( i2c_dev, LTC4274_REG_POWER_STATUS, psePwrGood); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE power good read failed.\n"); + } + if(LTC4274_PWRGD(*psePwrGood)) { + *psePwrGood = LTC4274_POWERGOOD; + } else { + *psePwrGood = LTC4274_POWERGOOD_NOTOK; + } + return status; +} + + + + +/***************************************************************************** + * Internal IRQ handler - reads in triggered interrupts and dispatches CBs + *****************************************************************************/ +static void ltc4274_handle_irq(void *context) { + LTC4274_Dev *dev = context; + uint8_t alertVal; + if (ltc4274_get_interrupt_status(&dev->cfg.i2c_dev, &alertVal) != RETURN_OK) { + /* Something really strange happened */ + return; + } + /* See if we have a callback assigned to handle alerts */ + if (!dev->obj.alert_cb) { + return; + } + switch(alertVal) { + case LTC4274_EVT_SUPPLY: + { + dev->obj.alert_cb(LTC4274_EVT_SUPPLY, dev->obj.cb_context); + } + break; + case LTC4274_EVT_TSTART: + { + dev->obj.alert_cb(LTC4274_EVT_TSTART, dev->obj.cb_context); + } + break; + case LTC4274_EVT_TCUT: + { + dev->obj.alert_cb(LTC4274_EVT_TCUT, dev->obj.cb_context); + } + break; + case LTC4274_EVT_CLASS: + { + dev->obj.alert_cb(LTC4274_EVT_CLASS, dev->obj.cb_context); + } + break; + case LTC4274_EVT_DETECTION: + { + dev->obj.alert_cb(LTC4274_EVT_DETECTION, dev->obj.cb_context); + } + break; + case LTC4274_EVT_DISCONNECT: + { + dev->obj.alert_cb(LTC4274_EVT_DISCONNECT, dev->obj.cb_context); + } + break; + case LTC4274_EVT_POWERGOOD: + { + dev->obj.alert_cb(LTC4274_EVT_POWERGOOD, dev->obj.cb_context); + } + break; + case LTC4274_EVT_POWER_ENABLE: + { + dev->obj.alert_cb(LTC4274_EVT_POWER_ENABLE, dev->obj.cb_context); + } + break; + default: + { + dev->obj.alert_cb(LTC4274_EVT_NONE, dev->obj.cb_context); + } + + } +} +/***************************************************************************** + *****************************************************************************/ +void ltc4274_set_alert_handler(LTC4274_Dev *dev, LTC4274_CallbackFn alert_cb, + void *cb_context) +{ + dev->obj.alert_cb = alert_cb; + dev->obj.cb_context = cb_context; +} + +/****************************************************************************** + * @fn ltc4274_clear_interrupt + * + * @brief Read powerGood info and power event info. + * + * @args A I2c device struct and address + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_clear_interrupt( const I2C_Dev *i2c_dev, + uint8_t *pwrEvent, + uint8_t *overCurrent, + uint8_t *supply) +{ + ReturnStatus status = RETURN_OK; + status = ltc4274_read( i2c_dev, LTC4274_REG_POWER_EVENT_COR, pwrEvent); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: Reading power good for PSE failed.\n"); + } + + /*Bit 4 for power good and bit 0 for power event*/ + LOGGER("PSELTC4274::INFO:: PSE power Good Info and Power ecent info is read with 0x%x.\n", + *pwrEvent); + + /* if it is due to over current*/ + status = ltc4274_read( i2c_dev, LTC4274_REG_START_EVENT_COR, overCurrent); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR::Reading power good for PSE failed.\n"); + } + + LOGGER("PSELTC4274::INFO:: PSE power Good Info and Power ecent info is read with 0x%x.\n", + *overCurrent); + + /* if its due to supply */ + status = ltc4274_read( i2c_dev, LTC4274_REG_SUPPLY_EVENT_COR, supply); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR::Reading power good for PSE failed.\n"); + } + + LOGGER("PSELTC4274::INFO:: PSE power Good Info and Power ecent info is read with 0x%x.\n", + *supply); + + return status; +} + +/****************************************************************************** + * @fn ltc4274_get_interrupt_status + * + * @brief Read active PSE interrupt info. + * + * @args I2c device struct and Address + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_get_interrupt_status(const I2C_Dev *i2c_dev, uint8_t *val) +{ + ReturnStatus status = RETURN_OK; + uint8_t interruptVal = 0; + status = ltc4274_read( i2c_dev, LTC4274_REG_INTERRUPT_STATUS, &interruptVal); + if (status != RETURN_OK) { + LOGGER("PSELTC4274: ERROR:Reading power good for PSE failed.\n"); + + } + LOGGER("PSELTC4274::INFO: PSE interrupt state is 0x%x.\n", interruptVal); + *val = interruptVal; + return status; +} + +/********************************************************************************* + * @fn ltc4274_debug_write + * + * @brief debug PSE device. + * + * @args I2c device struct, Address and value + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_debug_write(const I2C_Dev *i2c_dev, + uint8_t reg_address, uint8_t value) +{ + ReturnStatus status = RETURN_OK; + /*Enbale high power feature */ + status = ltc4274_write( i2c_dev, reg_address, value); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: Debug write command for reg 0x%x failed.\n", + reg_address); + } + return status; +} + +/********************************************************************************* + * @fn ltc4274_debug_read + * + * @brief debug PSE device. + * + * @args I2c device struct, Address and pointer to value + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_debug_read(const I2C_Dev *i2c_dev, + uint8_t reg_address, uint8_t *value) +{ + ReturnStatus status = RETURN_OK; + /*Enbale high power feature */ + status = ltc4274_read( i2c_dev, reg_address, value); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: Debug read command for reg 0x%x failed.\n", + reg_address); + } + return status; +} + + +/****************************************************************************** + * @fn ltc4274_enable + * + * @brief Enable . + *WR + * @args On/off + * + * @return void + */ +void ltc4274_enable(LTC4274_Dev* dev, uint8_t enableVal) +{ + if (enableVal) { + OcGpio_write(&dev->cfg.reset_pin, false); + } else { + OcGpio_write(&dev->cfg.reset_pin, true); + } +} + +/****************************************************************************** + * @fn ltc4274_get_devid + * + * @brief Read PSE device id. + * + * @args I2c device struct and Pointer to device Id. + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_get_devid(const I2C_Dev *i2c_dev, + uint8_t *devID) +{ + ReturnStatus ret = RETURN_OK; + ret = ltc4274_read(i2c_dev,LTC4274_REG_ID,devID); + if (ret != RETURN_OK) { + LOGGER("LTC4274:ERROR:: Reading Device Id for PSE failed.\n"); + } + *devID = LTC4274_DEVID(*devID); + return ret; +} + +/********************************************************************************* + * @fn ltc4274_detect + * + * @brief Reads the detectioin and classification register. + * + * @args I2c device struct and Addrress + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_detect(const I2C_Dev *i2c_dev, + uint8_t *detect, uint8_t *val) +{ + ReturnStatus status = RETURN_OK; + *val = 0; + status = ltc4274_read( i2c_dev, LTC4274_REG_DETECT_EVENT, detect); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR::Reading PSE port detection failed.\n"); + return status; + } + LOGGER("PSELTC4274::Reading PSE port detection done.\n"); + + status = ltc4274_read( i2c_dev, LTC4274_REG_STATUS, val); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR::Reading PSE port classificatin failed.\n"); + + } + LOGGER("PSELTC4274::Reading PSE port classification is 0x%x.\n", *val); + + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ltc4274_config + ** + ** DESCRIPTION : configure gpio and bring it out of reset . + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +void ltc4274_config(LTC4274_Dev *dev) +{ + OcGpio_configure(&dev->cfg.reset_pin, OCGPIO_CFG_OUTPUT | + OCGPIO_CFG_OUT_HIGH); + //Enable PSE device. + ltc4274_enable(dev,true); +} + +/***************************************************************************** + ** FUNCTION NAME : ltc4274_probe + ** + ** DESCRIPTION : reset PSE device. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +ePostCode ltc4274_probe(const LTC4274_Dev *dev, POSTData *postData) +{ + ePostCode postcode = POST_DEV_MISSING; + uint8_t devId = 0x00; + ReturnStatus status = RETURN_NOTOK; + status = ltc4274_get_devid(&dev->cfg.i2c_dev, &devId); + if (status != RETURN_OK) { + postcode = POST_DEV_MISSING; + } else if (devId == LTC4274_DEV_ID){ + postcode = POST_DEV_FOUND; + } else { + postcode = POST_DEV_ID_MISMATCH; + } + post_update_POSTData(postData, dev->cfg.i2c_dev.bus, dev->cfg.i2c_dev.slave_addr,0xFF, devId); + return postcode; +} + +/***************************************************************************** + ** FUNCTION NAME : ltc4274_reset + ** + ** DESCRIPTION : reset PSE device. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : Success or Failure + ** + *****************************************************************************/ +ReturnStatus ltc4274_reset(LTC4274_Dev *dev) +{ + ReturnStatus status = RETURN_OK; + + + OcGpio_write(&dev->cfg.reset_pin, true); + Task_sleep(100); + OcGpio_write(&dev->cfg.reset_pin, false); + + + return status; +} + +/****************************************************************************** + * @fn ltc4274_default_cfg + * + * @brief configure PSE device. + * + * @args I2c device struct and PSE device configurations. + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_default_cfg( const I2C_Dev *i2c_dev, + uint8_t operatingMode, + uint8_t detectEnable, + uint8_t intrMask, + uint8_t interruptEnable, + uint8_t hpEnable) +{ + ReturnStatus ret = RETURN_OK; + ret = ltc4274_set_cfg_operation_mode(i2c_dev,operatingMode); + if (ret != RETURN_OK) { + LOGGER("LTC4274::ERROR: PSE operational mode setting mode failed.\n"); + return ret; + } + + ret = ltc4274_set_cfg_detect_enable(i2c_dev,detectEnable); + if (ret != RETURN_OK) { + LOGGER("LTC4274::ERROR: PSE detection and classification enable failed.\n"); + return ret; + } + + ret = ltc4274_set_interrupt_mask(i2c_dev,intrMask); + if (ret != RETURN_OK) { + LOGGER("LTC4274::ERROR:PSE interrupts mask enable failed.\n"); + return ret; + } + + ret = ltc4274_cfg_interrupt_enable(i2c_dev,interruptEnable); + if (ret != RETURN_OK) { + LOGGER("LTC4274::ERROR: PSE interrupt enable failed.\n"); + return ret; + } + + ret = ltc4274_set_cfg_pshp_feature(i2c_dev,hpEnable); + if (ret != RETURN_OK) { + LOGGER("LTC4274::ERROR: PSE configured for LTEPOE++.\n"); + return ret; + } + return ret; +} + +/***************************************************************************** + ** FUNCTION NAME : ltc4274_init + ** + ** DESCRIPTION : PSE Status is intialized. + ** + ** ARGUMENTS : None. + ** + ** RETURN TYPE : ReturnStatus + ** + *****************************************************************************/ +void ltc4274_init(LTC4274_Dev *dev) +{ + dev->obj = (LTC4274_Obj){}; + + dev->obj.mutex = GateMutex_create(NULL, NULL); + if (!dev->obj.mutex) { + return; + } + + ltc4274_initPSEStateInfo(); + + if (dev->cfg.pin_evt) { + const uint32_t pin_evt_cfg = OCGPIO_CFG_INPUT | OCGPIO_CFG_INT_FALLING; + if (OcGpio_configure(dev->cfg.pin_evt, pin_evt_cfg) < OCGPIO_SUCCESS) { + return ; + } + + /* Use a threaded interrupt to handle IRQ */ + // ThreadedInt_Init(dev->cfg.pin_evt, ltc4274_handle_irq, (void *)dev); + } +} +/***************************************************************************** + ** FUNCTION NAME : ltc4274_initPSEStateInfo + ** + ** DESCRIPTION : PSE Status is intialized. + ** + ** ARGUMENTS : None. + ** + ** RETURN TYPE : ReturnStatus + ** + *****************************************************************************/ +void ltc4274_initPSEStateInfo() +{ + PSEStatus_Info.pseStatus.detectStatus = LTC4274_DETECT_UNKOWN; + PSEStatus_Info.pseStatus.classStatus = LTC4274_CLASSTYPE_UNKOWN; + PSEStatus_Info.pseStatus.powerGoodStatus = LTC4274_POWERGOOD_NOTOK; + PSEStatus_Info.psealert = LTC4274_DISCONNECT_ALERT; +} + +/***************************************************************************** + ** FUNCTION NAME : ltc4274_update_state + ** + ** DESCRIPTION : PSE Status to update. + ** + ** ARGUMENTS : I2c struct + ** + ** RETURN TYPE : void + ** + *****************************************************************************/ +void ltc4274_update_stateInfo(const I2C_Dev *i2c_dev) +{ + ReturnStatus status = RETURN_OK; + status = ltc4274_get_powergood_status( i2c_dev, + &PSEStatus_Info.pseStatus.powerGoodStatus); + if (status != RETURN_OK) { + LOGGER("PDLTC4275::ERROR: Power good signal read failed.\n"); + PSEStatus_Info.pseStatus.detectStatus = LTC4274_DETECT_UNKOWN; + PSEStatus_Info.pseStatus.classStatus = LTC4274_CLASSTYPE_UNKOWN; + PSEStatus_Info.psealert = LTC4274_DISCONNECT_ALERT; + return; + } + if (PSEStatus_Info.pseStatus.powerGoodStatus == LTC4274_POWERGOOD) { + status = ltc4274_get_detection_status(i2c_dev, &PSEStatus_Info.pseStatus.detectStatus); + if (status != RETURN_OK) { + LOGGER("PDLTC4275::ERROR: Reading PSE detection failed.\n"); + PSEStatus_Info.pseStatus.detectStatus = LTC4274_DETECT_UNKOWN; + PSEStatus_Info.pseStatus.classStatus = LTC4274_CLASSTYPE_UNKOWN; + PSEStatus_Info.psealert = LTC4274_DISCONNECT_ALERT; + return; + } + status = ltc4274_get_class_status(i2c_dev, &PSEStatus_Info.pseStatus.classStatus); + if (status != RETURN_OK) { + LOGGER("PDLTC4275::ERROR: Reading PSE classification failed.\n"); + PSEStatus_Info.pseStatus.detectStatus = LTC4274_DETECT_UNKOWN; + PSEStatus_Info.pseStatus.classStatus = LTC4274_CLASSTYPE_UNKOWN; + PSEStatus_Info.psealert = LTC4274_DISCONNECT_ALERT; + return; + } + status = ltc4274_get_interrupt_status(i2c_dev, &PSEStatus_Info.psealert); + if (status != RETURN_OK) { + LOGGER("PDLTC4275::ERROR: Reading PSE detection failed.\n"); + PSEStatus_Info.pseStatus.detectStatus = LTC4274_DETECT_UNKOWN; + PSEStatus_Info.pseStatus.classStatus = LTC4274_CLASSTYPE_UNKOWN; + PSEStatus_Info.psealert = LTC4274_DISCONNECT_ALERT; + return; + } + } +} diff --git a/firmware/psu/src/devices/ltc4295.c b/firmware/psu/src/devices/ltc4295.c new file mode 100644 index 0000000000..5e7c972358 --- /dev/null +++ b/firmware/psu/src/devices/ltc4295.c @@ -0,0 +1,266 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** +#include "devices/i2c/threaded_int.h" +#include "helpers/math.h" +#include "helpers/memory.h" +#include "inc/common/byteorder.h" +#include "inc/devices/ltc4295.h" +#include "inc/subsystem/power/power.h" + +#include +#include + +tPower_PDStatus_Info PDStatus_Info; + + +/****************************************************************************** + * @fn ltc4295_handle_irq + * + * @brief Read the change in the PD state and callbacks the registerd function. + * + * @args Alert Context + * + * @return None + */ +static void ltc4295_handle_irq(void *context) { + LTC4295_Dev *dev = context; + + const IArg mutexKey = GateMutex_enter(dev->obj.mutex); { + ltc4295_update_status(dev); + } GateMutex_leave(dev->obj.mutex, mutexKey); + + /* See if we have a callback assigned to handle alerts */ + if (!dev->obj.alert_cb) { + return; + } + + /* Since > CRIT implies above window, we only handle the highest priority + * event to avoid duplicate events being sent */ + if (PDStatus_Info.pdalert == LTC4295_CONNECT_ALERT) { + dev->obj.alert_cb(LTC4295_CONNECT_EVT, dev->obj.cb_context); + } else if (PDStatus_Info.pdalert == LTC4295_DISCONNECT_ALERT) { + dev->obj.alert_cb(LTC4295_DISCONNECT_EVT, dev->obj.cb_context); + } else if (PDStatus_Info.pdalert == LTC4295_INCOMPATIBLE_ALERT) { + dev->obj.alert_cb(LTC4295_INCOMPATIBLE_EVT, dev->obj.cb_context); + } +} + +/****************************************************************************** + * @fn ltc4295_configure + * + * @brief configure GPIO's. + * + * @args None + * + * @return None + */ +void ltc4295_config(const LTC4295_Dev *dev) +{ + OcGpio_configure(dev->cfg.pin_evt, OCGPIO_CFG_INPUT); + OcGpio_configure(dev->cfg.pin_detect, OCGPIO_CFG_INPUT); +} + +/****************************************************************************** + * @fn ltc4295_probe + * + * @brief Intializes PD update struct. + * + * @args None + * + * @return None + */ +ePostCode ltc4295_probe(const LTC4295_Dev *dev, POSTData *postData) +{ + ePostCode postCode = POST_DEV_MISSING; + ePDPowerState pdStatus = LTC4295_POWERGOOD_NOTOK; + ReturnStatus ret = ltc4295_get_power_good(dev, &pdStatus); + if (ret != RETURN_OK) { + LOGGER("LTC4295::ERROR: Power good signal read failed.\n"); + return postCode; + } + if (pdStatus == LTC4295_POWERGOOD ) { + PDStatus_Info.pdStatus.classStatus = LTC4295_CLASSTYPE_UNKOWN; + PDStatus_Info.pdStatus.powerGoodStatus = LTC4295_POWERGOOD; + PDStatus_Info.state = LTC4295_STATE_NOTOK; + PDStatus_Info.pdalert = LTC4295_DISCONNECT_ALERT; + postCode = POST_DEV_FOUND; + } else { + PDStatus_Info.pdStatus.classStatus = LTC4295_CLASSTYPE_UNKOWN; + PDStatus_Info.pdStatus.powerGoodStatus = LTC4295_POWERGOOD_NOTOK; + PDStatus_Info.state = LTC4295_STATE_NOTOK; + PDStatus_Info.pdalert = LTC4295_DISCONNECT_ALERT; + } + post_update_POSTData(postData, 0xFF, 0xFF, 0xFF, 0xFF); + return postCode; +} + +/****************************************************************************** + * @fn ltc4295_init + * + * @brief Intializes PD update struct. + * + * @args None + * + * @return None + */ +ReturnStatus ltc4295_init(LTC4295_Dev *dev) +{ + ReturnStatus ret = RETURN_OK; + dev->obj = (LTC4295_Obj){}; + + dev->obj.mutex = GateMutex_create(NULL, NULL); + if (!dev->obj.mutex) { + return RETURN_NOTOK; + } + + ret = ltc4295_get_power_good(dev, &PDStatus_Info.pdStatus.powerGoodStatus); + if (ret != RETURN_OK) { + LOGGER("LTC4295::ERROR: Power good signal read failed.\n"); + return ret; + } + if (PDStatus_Info.pdStatus.powerGoodStatus == LTC4295_POWERGOOD) { + ret = ltc4295_get_class(dev, &PDStatus_Info.pdStatus.classStatus); + if (ret != RETURN_OK) { + LOGGER("LTC4295::ERROR: Reading PD classification failed.\n"); + return ret; + } + if (PDStatus_Info.pdStatus.classStatus == LTC4295_CLASSTYPE_POEPP) { + PDStatus_Info.state = LTC4295_STATE_OK; + } + } + + if (dev->cfg.pin_evt) { + const uint32_t pin_evt_cfg = OCGPIO_CFG_INPUT | OCGPIO_CFG_INT_BOTH_EDGES ; + if (OcGpio_configure(dev->cfg.pin_evt, pin_evt_cfg) < OCGPIO_SUCCESS) { + return RETURN_NOTOK; + } + + /* Use a threaded interrupt to handle IRQ */ + // ThreadedInt_Init(dev->cfg.pin_evt, ltc4295_handle_irq, (void *)dev); + } + return ret; +} + +/****************************************************************************** + * @fn ltc4295_set_alert_handler + * + * @brief Set the alert callback function and context. + * + * @args Device, callBack function and context + * + * @return None + */ +void ltc4295_set_alert_handler(LTC4295_Dev *dev, LTC4295_CallbackFn alert_cb, + void *cb_context) +{ + dev->obj.alert_cb = alert_cb; + dev->obj.cb_context = cb_context; +} + +/****************************************************************************** + * @fn ltc4295_get_power_good + * + * @brief Read GPIO status based on that decide power good signal. + * + * @args Addrress + * + * @return ReturnStatus + */ +ReturnStatus ltc4295_get_power_good(const LTC4295_Dev *dev, ePDPowerState *val) +{ + ReturnStatus ret = RETURN_OK; + /*set default to 1*/ + *val = LTC4295_POWERGOOD_NOTOK; + + /* Check Power Good */ + *val = (ePDPowerState) OcGpio_read(dev->cfg.pin_evt); + if(*val == 0) + { + *val = LTC4295_POWERGOOD; + } + DEBUG("LTC4295:INFO:: PD power good is %d.\n", *val); + return ret; +} + +/****************************************************************************** + * @fn ltc4295_get_class + * + * @brief ReadGPIO status based on that decide the PD class. + * + * @args Addrress + * + * @return ReturnStatus + */ +ReturnStatus ltc4295_get_class(const LTC4295_Dev *dev, ePDClassType *val) +{ + ReturnStatus ret = RETURN_OK; + uint8_t i = 0; + uint8_t value = 1; + uint8_t prev_value = 1; + uint8_t toggle = 0; + + for (i = 0; i < 15; i++) { + value = OcGpio_read(dev->cfg.pin_detect); + LOGGER_DEBUG("LTC4295:INFO:: PD-nT2P activity status %d.\n", value); + if (value == 1) { + *val = LTC4295_CLASSTYPE_2; + } else if (value == 0) { + *val = LTC4295_CLASSTYPE_1; + } + /*Incremented only in the case of POE++ device*/ + if (prev_value != value) { + toggle++; + } + prev_value = value; + Task_sleep(3); + } + if (toggle > 2) { + *val = LTC4295_CLASSTYPE_POEPP; + } + LOGGER("LTC4295:INFO:: PD detects POE of class 0x%x.\n", *val); + return ret; +} + +/****************************************************************************** + * @fn ltc4295_update_status + * + * @brief Maintains the PS status. + * + * @args CLass and power good state of PD. + * + * @return None + */ +void ltc4295_update_status(const LTC4295_Dev *dev) +{ + ReturnStatus ret = RETURN_NOTOK; + ret = ltc4295_get_power_good(dev,&PDStatus_Info.pdStatus.powerGoodStatus); + if (ret != RETURN_OK) { + LOGGER("LTC4295::ERROR: Power good signal read failed.\n"); + return; + } + if (PDStatus_Info.pdStatus.powerGoodStatus == LTC4295_POWERGOOD) { + ret = ltc4295_get_class(dev, &PDStatus_Info.pdStatus.classStatus); + if (ret != RETURN_OK) { + LOGGER("LTC4295::ERROR: Reading PD classification failed.\n"); + return; + } + if (PDStatus_Info.pdStatus.classStatus == LTC4295_CLASSTYPE_POEPP) { + PDStatus_Info.state = LTC4295_STATE_OK; + PDStatus_Info.pdalert = LTC4295_CONNECT_ALERT; + } + } else { + PDStatus_Info.state = LTC4295_STATE_NOTOK; + PDStatus_Info.pdalert = LTC4295_DISCONNECT_ALERT; + PDStatus_Info.pdStatus.classStatus == LTC4295_CLASSTYPE_UNKOWN; + } +} diff --git a/firmware/psu/src/devices/ocmp_wrappers/ocmp_cat24c04.c b/firmware/psu/src/devices/ocmp_wrappers/ocmp_cat24c04.c new file mode 100644 index 0000000000..378494bf32 --- /dev/null +++ b/firmware/psu/src/devices/ocmp_wrappers/ocmp_cat24c04.c @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "common/inc/ocmp_wrappers/ocmp_eeprom_cat24c04.h" + +#include "common/inc/global/Framework.h" +#include "inc/common/global_header.h" +#include "inc/devices/eeprom.h" + +#include + +static ePostCode _init_eeprom(void *driver, const void **config, + const void *alert_token) +{ + Eeprom_Cfg *eeprom = (Eeprom_Cfg *)driver; + uint8_t write = 0x01; + uint8_t read = 0x00; + + eeprom_init(eeprom); + eeprom_enable_write(eeprom); + eeprom_write(eeprom, OC_TEST_ADDRESS, &write, 1); + NOP_DELAY(); /* TODO: the eeprom driver should handle this */ + eeprom_disable_write(eeprom); + eeprom_read(eeprom, OC_TEST_ADDRESS, &read, 1); + + if (write == read) { + return POST_DEV_CFG_DONE; + } + return POST_DEV_CFG_FAIL; +} + +const Driver_fxnTable CAT24C04_psu_sid_fxnTable = { + /* Message handlers */ + .cb_init = _init_eeprom, +}; + +const Driver_fxnTable CAT24C04_psu_inv_fxnTable= { + .cb_init = _init_eeprom, +}; diff --git a/firmware/psu/src/devices/ocmp_wrappers/ocmp_debugi2c.c b/firmware/psu/src/devices/ocmp_wrappers/ocmp_debugi2c.c new file mode 100644 index 0000000000..5c197a3126 --- /dev/null +++ b/firmware/psu/src/devices/ocmp_wrappers/ocmp_debugi2c.c @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#include "common/inc/global/ocmp_frame.h" +#include "common/inc/global/Framework.h" +#include "inc/common/global_header.h" +#include "inc/common/i2cbus.h" +#include "inc/devices/debug_oci2c.h" +#include "inc/ocmp_wrappers/ocmp_debugi2c.h" + +/* TI-RTOS driver files */ +#include + +/***************************************************************************** + ** FUNCTION NAME : i2c_read + ** + ** DESCRIPTION : i2c read + ** + ** ARGUMENTS : i2c bus config, i2c config + ** + ** RETURN TYPE : RETURN_OK or RETURN_NOTOK + ** + *****************************************************************************/ +bool i2c_read(void* i2c_cfg, void *oci2c ) +{ + S_I2C_Cfg* s_oc_i2c_cfg = (S_I2C_Cfg*)i2c_cfg; + S_OCI2C* s_oci2c = (S_OCI2C*)oci2c; + I2C_Handle i2cHandle = i2c_open_bus(s_oc_i2c_cfg->bus); + return (i2c_reg_read(i2cHandle, s_oci2c->slaveAddress, s_oci2c->reg_address, &s_oci2c->reg_value, s_oci2c->number_of_bytes) == RETURN_OK); +} + +/***************************************************************************** + ** FUNCTION NAME : i2c_write + ** + ** DESCRIPTION : i2c write + ** + ** ARGUMENTS : i2c bus config, i2c config + ** + ** RETURN TYPE : RETURN_OK or RETURN_NOTOK + ** + *****************************************************************************/ +bool i2c_write(void* i2c_cfg, void *oci2c ) +{ + S_I2C_Cfg* s_oc_i2c_cfg = (S_I2C_Cfg*)i2c_cfg; + S_OCI2C* s_oci2c = (S_OCI2C*)oci2c; + I2C_Handle i2cHandle = i2c_open_bus(s_oc_i2c_cfg->bus); + return (i2c_reg_write(i2cHandle, s_oci2c->slaveAddress, s_oci2c->reg_address, s_oci2c->reg_value, s_oci2c->number_of_bytes) == RETURN_OK); +} diff --git a/firmware/psu/src/devices/ocmp_wrappers/ocmp_debugocgpio.c b/firmware/psu/src/devices/ocmp_wrappers/ocmp_debugocgpio.c new file mode 100644 index 0000000000..173efe2ff3 --- /dev/null +++ b/firmware/psu/src/devices/ocmp_wrappers/ocmp_debugocgpio.c @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "common/inc/global/Framework.h" +#include "common/inc/global/ocmp_frame.h" +#include "inc/common/global_header.h" +#include "inc/devices/debug_ocgpio.h" +#include "inc/ocmp_wrappers/ocmp_debugocgpio.h" +#include + +#define NO_GPIO_PINS_IN_GROUP 8 +extern GPIO_PinConfig gpioPinConfigs[]; + +/***************************************************************************** + ** FUNCTION NAME : ocgpio_set + ** + ** DESCRIPTION : i2c read + ** + ** ARGUMENTS : i2c bus config, i2c config + ** + ** RETURN TYPE : RETURN_OK or RETURN_NOTOK + ** + *****************************************************************************/ +bool ocgpio_set(void* gpio_cfg, void* oc_gpio ) +{ + S_OCGPIO_Cfg* oc_gpio_cfg = (S_OCGPIO_Cfg*)gpio_cfg; + S_OCGPIO* s_oc_gpio = (S_OCGPIO*)oc_gpio; + int ret = 0; + uint8_t idx = ((oc_gpio_cfg->group != 0)?(((oc_gpio_cfg->group-1) * NO_GPIO_PINS_IN_GROUP) + s_oc_gpio->pin):s_oc_gpio->pin); + // OcGpio_Pin ocgpio = { (oc_gpio_cfg->port), idx, ((oc_gpio_cfg->group != 0)?(gpioPinConfigs[idx]>>16):OCGPIO_CFG_OUT_STD)}; + OcGpio_Pin ocgpio = { (oc_gpio_cfg->port), idx, OCGPIO_CFG_OUT_STD}; + ret = OcGpio_configure(&ocgpio, OCGPIO_CFG_OUTPUT); + ret = OcGpio_write(&ocgpio,s_oc_gpio->value); + return (ret == 0); +} + +/***************************************************************************** + ** FUNCTION NAME : ocgpio_get + ** + ** DESCRIPTION : gpio read + ** + ** ARGUMENTS : i2c bus config, i2c config + ** + ** RETURN TYPE : RETURN_OK or RETURN_NOTOK + ** + *****************************************************************************/ +bool ocgpio_get(void* gpio_cfg, void* oc_gpio ) +{ + S_OCGPIO_Cfg* oc_gpio_cfg = (S_OCGPIO_Cfg*)gpio_cfg; + S_OCGPIO* s_oc_gpio = (S_OCGPIO*)oc_gpio; + int ret = 0; + uint8_t idx = ((oc_gpio_cfg->group != 0)?(((oc_gpio_cfg->group-1) * NO_GPIO_PINS_IN_GROUP) + s_oc_gpio->pin):s_oc_gpio->pin); +// OcGpio_Pin ocgpio = { (oc_gpio_cfg->port), idx, ((oc_gpio_cfg->group!= 0)?(gpioPinConfigs[idx]>>16):OCGPIO_CFG_IN_PU)}; + OcGpio_Pin ocgpio = { (oc_gpio_cfg->port), idx, 0}; + ret = OcGpio_configure(&ocgpio, OCGPIO_CFG_INPUT); + s_oc_gpio->value = OcGpio_read(&ocgpio); + if ( s_oc_gpio->value < 0) { + ret = -1; + } + return (ret == 0); +} + +/***************************************************************************** + ** FUNCTION NAME : _probe + ** + ** DESCRIPTION : ocmp wrapper for handling POST(power on self test) + ** + ** ARGUMENTS : driver , postData + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _probe(S_OCGPIO_Cfg* oc_gpio_cfg) +{ + if (OcGpio_probe(oc_gpio_cfg->port) != 0) { + return POST_DEV_MISSING; + } else { + return POST_DEV_FOUND; + } +} + +/***************************************************************************** + ** FUNCTION NAME : _init + ** + ** DESCRIPTION : ocmp wrapper for handling init + ** + ** ARGUMENTS : driver , driver config, context for cb function + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _init(void *driver, const void *config, + const void *alert_token) +{ + return POST_DEV_CFG_DONE; +} + +const Driver_fxnTable DEBUG_OCGPIO_fxnTable = { + /* Message handlers */ + .cb_probe = _probe, + .cb_init = _init, +}; diff --git a/firmware/psu/src/devices/ocmp_wrappers/ocmp_ina226.c b/firmware/psu/src/devices/ocmp_wrappers/ocmp_ina226.c new file mode 100644 index 0000000000..a0540fc9c3 --- /dev/null +++ b/firmware/psu/src/devices/ocmp_wrappers/ocmp_ina226.c @@ -0,0 +1,186 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "common/inc/global/Framework.h" +#include "common/inc/ocmp_wrappers/ocmp_ina226.h" +#include "inc/devices/ina226.h" + +typedef enum INA226Status { + INA226_STATUS_BUS_VOLTAGE = 0, + INA226_STATUS_SHUNT_VOLTAGE, + INA226_STATUS_CURRENT, + INA226_STATUS_POWER, +} INA226Status; + +typedef enum INA226Config { + INA226_CONFIG_CURRENT_LIM = 0, +} INA226Config; + +typedef enum INA226Alert { + INA226_ALERT_OVERCURRENT = 0, +} INA226Alert; + +/***************************************************************************** + ** FUNCTION NAME : _get_status + ** + ** DESCRIPTION : ocmp wrapper for getting status parameter + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _get_status(void *driver, unsigned int param_id, + void *return_buf) { + switch (param_id) { + case INA226_STATUS_BUS_VOLTAGE: { + uint16_t *res = return_buf; + return (ina226_readBusVoltage(driver, res) == RETURN_OK); + } + case INA226_STATUS_SHUNT_VOLTAGE: { + uint16_t *res = return_buf; + return (ina226_readShuntVoltage(driver, res) == RETURN_OK); + } + case INA226_STATUS_CURRENT: { + uint16_t *res = return_buf; + return (ina226_readCurrent(driver, res) == RETURN_OK); + } + case INA226_STATUS_POWER: { + uint16_t *res = return_buf; + return (ina226_readPower(driver, res) == RETURN_OK); + } + default: + LOGGER_ERROR("INA226::Unknown status param %d\n", param_id); + return false; + } +} + +/***************************************************************************** + ** FUNCTION NAME : _get_config + ** + ** DESCRIPTION : ocmp wrapper for getting config parameter + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _get_config(void *driver, unsigned int param_id, + void *return_buf) { + switch (param_id) { + case INA226_CONFIG_CURRENT_LIM: { + uint16_t *res = return_buf; + return (ina226_readCurrentLim(driver, res) == RETURN_OK); + } + default: + LOGGER_ERROR("INA226::Unknown config param %d\n", param_id); + return false; + } +} + +/***************************************************************************** + ** FUNCTION NAME : _set_config + ** + ** DESCRIPTION : ocmp wrapper for setting config parameter + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _set_config(void *driver, unsigned int param_id, + const void *data) { + switch (param_id) { + case INA226_CONFIG_CURRENT_LIM: { + const uint16_t *limit = data; + return (ina226_setCurrentLim(driver, *limit) == RETURN_OK); + } + default: + LOGGER_ERROR("INA226::Unknown config param %d\n", param_id); + return false; + } +} + +/***************************************************************************** + ** FUNCTION NAME : _probe + ** + ** DESCRIPTION : ocmp wrapper for handling POST(power on self test) + ** + ** ARGUMENTS : driver , postData + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _probe(void *driver, POSTData *postData) +{ + return ina226_probe(driver,postData); +} + +/***************************************************************************** + ** FUNCTION NAME : _alert_handler + ** + ** DESCRIPTION : call back function for handling alerts from device + ** layer + ** + ** ARGUMENTS : event type , current value, alert config, + ** + ** RETURN TYPE : + ** + *****************************************************************************/ +static void _alert_handler(INA226_Event evt, uint16_t value, void *alert_data) +{ + if (evt != INA226_EVT_COL) { + LOGGER_WARNING("IN226::Unsupported INA event 0x%x\n", evt); + return; + } + + OCMP_GenerateAlert(alert_data, INA226_ALERT_OVERCURRENT, &value); + LOGGER_DEBUG("INA226 Event: 0x%x Current: %u\n", evt, value); +} + +/***************************************************************************** + ** FUNCTION NAME : _init + ** + ** DESCRIPTION : ocmp wrapper for handling init + ** + ** ARGUMENTS : driver , driver config, context for cb function + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _init(void *driver, const void *config, + const void *alert_token) +{ + if (ina226_init(driver) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + + if (!config) { + return POST_DEV_CFG_DONE; + } + const INA226_Config *ina226_config = config; + if (ina226_setCurrentLim(driver, ina226_config->current_lim) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + + ina226_setAlertHandler(driver, _alert_handler, (void *)alert_token); + if (ina226_enableAlert(driver, INA226_EVT_COL) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + + return POST_DEV_CFG_DONE; +} + +const Driver_fxnTable INA226_fxnTable = { + /* Message handlers */ + .cb_probe = _probe, + .cb_init = _init, + .cb_get_status = _get_status, + .cb_get_config = _get_config, + .cb_set_config = _set_config, +}; diff --git a/firmware/psu/src/devices/ocmp_wrappers/ocmp_ltc4015.c b/firmware/psu/src/devices/ocmp_wrappers/ocmp_ltc4015.c new file mode 100644 index 0000000000..3a9e4a8646 --- /dev/null +++ b/firmware/psu/src/devices/ocmp_wrappers/ocmp_ltc4015.c @@ -0,0 +1,350 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "common/inc/ocmp_wrappers/ocmp_ltc4015.h" +#include "inc/devices/ltc4015.h" + +typedef enum LTC4015Status { + LTC4015_STATUS_BATTERY_VOLTAGE = 0, + LTC4015_STATUS_BATTERY_CURRENT, + LTC4015_STATUS_SYSTEM_VOLTAGE, + LTC4015_STATUS_INPUT_VOLATGE, + LTC4015_STATUS_INPUT_CURRENT, + LTC4015_STATUS_DIE_TEMPERATURE, + LTC4015_STATUS_ICHARGE_DAC +} LTC4015Status; + +typedef enum LTC4015Config { + LTC4015_CONFIG_BATTERY_VOLTAGE_LOW = 0, + LTC4015_CONFIG_BATTERY_VOLTAGE_HIGH, + LTC4015_CONFIG_BATTERY_CURRENT_LOW, + LTC4015_CONFIG_INPUT_VOLTAGE_LOW, + LTC4015_CONFIG_INPUT_CURRENT_HIGH, + LTC4015_CONFIG_INPUT_CURRENT_LIMIT, + LTC4015_CONFIG_ICHARGE, + LTC4015_CONFIG_VCHARGE, + LTC4015_CONFIG_DIE_TEMPERATURE_HIGH, +} LTC4015Config; + +typedef enum LTC4015Alert { + LTC4015_ALERT_BATTERY_VOLTAGE_LOW = 0, + LTC4015_ALERT_BATTERY_VOLTAGE_HIGH, + LTC4015_ALERT_BATTERY_CURRENT_LOW, + LTC4015_ALERT_INPUT_VOLTAGE_LOW, + LTC4015_ALERT_INPUT_CURRENT_HIGH, + LTC4015_ALERT_DIE_TEMPERATURE_HIGH, +} LTC4015Alert; + +#if 0 +static bool _choose_battery_charger(LTC4015_Dev *dev) { + if (OcGpio_write(&dev->cfg.pin_lt4015_i2c_sel, + (dev->cfg.chem == LTC4015_CHEM_LI_ION)) < OCGPIO_SUCCESS) { + return false; + } + return true; +} +#endif +/***************************************************************************** + ** FUNCTION NAME : _get_status + ** + ** DESCRIPTION : ocmp wrapper for getting status + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _get_status(void *driver, unsigned int param_id, + void *return_buf) { + // if(!_choose_battery_charger(driver)) + // return false; + + switch (param_id) { + case LTC4015_STATUS_BATTERY_VOLTAGE: { + int16_t *res = return_buf; + return (LTC4015_get_battery_voltage(driver, res) == RETURN_OK); + } + case LTC4015_STATUS_BATTERY_CURRENT: { + int16_t *res = return_buf; + return (LTC4015_get_battery_current(driver, res) == RETURN_OK); + } + case LTC4015_STATUS_SYSTEM_VOLTAGE: { + int16_t *res = return_buf; + return (LTC4015_get_system_voltage(driver, res) == RETURN_OK); + } + case LTC4015_STATUS_INPUT_VOLATGE: { + int16_t *res = return_buf; + return (LTC4015_get_input_voltage(driver, res) == RETURN_OK); + } + case LTC4015_STATUS_INPUT_CURRENT: { + int16_t *res = return_buf; + return (LTC4015_get_input_current(driver, res) == RETURN_OK); + } + case LTC4015_STATUS_DIE_TEMPERATURE: { + int16_t *res = return_buf; + return (LTC4015_get_die_temperature(driver, res) == RETURN_OK); + } + case LTC4015_STATUS_ICHARGE_DAC: { + int16_t *res = return_buf; + return (LTC4015_get_icharge_dac(driver, res) == RETURN_OK); + } + default: + LOGGER_ERROR("LTC4015::Unknown status param %d\n", param_id); + return false; + } +} +/***************************************************************************** + ** FUNCTION NAME : _get_config + ** + ** DESCRIPTION : ocmp wrapper for getting config parameter + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _get_config(void *driver, unsigned int param_id, + void *return_buf) { + + switch (param_id) { + case LTC4015_CONFIG_BATTERY_VOLTAGE_LOW: { + int16_t *res = return_buf; + return (LTC4015_get_cfg_battery_voltage_low(driver, res) + == RETURN_OK); + } + case LTC4015_CONFIG_BATTERY_VOLTAGE_HIGH: { + int16_t *res = return_buf; + return (LTC4015_get_cfg_battery_voltage_high(driver, res) + == RETURN_OK); + } + case LTC4015_CONFIG_BATTERY_CURRENT_LOW: { + int16_t *res = return_buf; + return (LTC4015_get_cfg_battery_current_low(driver, res) + == RETURN_OK); + } + case LTC4015_CONFIG_INPUT_VOLTAGE_LOW: { + int16_t *res = return_buf; + return (LTC4015_get_cfg_input_voltage_low(driver, res) + == RETURN_OK); + } + case LTC4015_CONFIG_INPUT_CURRENT_HIGH: { + int16_t *res = return_buf; + return (LTC4015_get_cfg_input_current_high(driver, res) + == RETURN_OK); + } + case LTC4015_CONFIG_INPUT_CURRENT_LIMIT: { + uint16_t *res = return_buf; + return (LTC4015_get_cfg_input_current_limit(driver, res) + == RETURN_OK); + } + case LTC4015_CONFIG_ICHARGE: { + uint16_t *res = return_buf; + return (LTC4015_get_cfg_icharge(driver, res) == RETURN_OK); + } + case LTC4015_CONFIG_VCHARGE: { + uint16_t *res = return_buf; + return (LTC4015_get_cfg_vcharge(driver, res) == RETURN_OK); + } + case LTC4015_CONFIG_DIE_TEMPERATURE_HIGH: { + int16_t *res = return_buf; + return (LTC4015_get_cfg_die_temperature_high(driver, res) + == RETURN_OK); + } + default: + LOGGER_ERROR("LTC4015::Unknown config param %d\n", param_id); + return false; + } +} +/***************************************************************************** + ** FUNCTION NAME : _set_config + ** + ** DESCRIPTION : ocmp wrapper for setting config parameter + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _set_config(void *driver, unsigned int param_id, + const void *data) { + + switch (param_id) { + case LTC4015_CONFIG_BATTERY_VOLTAGE_LOW: { + const int16_t *limit = data; + return (LTC4015_cfg_battery_voltage_low(driver, *limit) + == RETURN_OK); + } + case LTC4015_CONFIG_BATTERY_VOLTAGE_HIGH: { + const int16_t *limit = data; + return (LTC4015_cfg_battery_voltage_high(driver, *limit) + == RETURN_OK); + } + case LTC4015_CONFIG_BATTERY_CURRENT_LOW: { + const int16_t *limit = data; + return (LTC4015_cfg_battery_current_low(driver, *limit) + == RETURN_OK); + } + case LTC4015_CONFIG_INPUT_VOLTAGE_LOW: { + const int16_t *limit = data; + return (LTC4015_cfg_input_voltage_low(driver, *limit) + == RETURN_OK); + } + case LTC4015_CONFIG_INPUT_CURRENT_HIGH: { + const int16_t *limit = data; + return (LTC4015_cfg_input_current_high(driver, *limit) + == RETURN_OK); + } + case LTC4015_CONFIG_INPUT_CURRENT_LIMIT: { + const uint16_t *limit = data; + return (LTC4015_cfg_input_current_limit(driver, *limit) + == RETURN_OK); + } + case LTC4015_CONFIG_ICHARGE: { + const uint16_t *limit = data; + return (LTC4015_cfg_icharge(driver, *limit) == RETURN_OK); + } + case LTC4015_CONFIG_VCHARGE: { + const uint16_t *limit = data; + return (LTC4015_cfg_vcharge(driver, *limit) == RETURN_OK); + } + case LTC4015_CONFIG_DIE_TEMPERATURE_HIGH: { + const int16_t *limit = data; + return (LTC4015_cfg_die_temperature_high(driver, *limit) + == RETURN_OK); + } + default: + LOGGER_ERROR("LTC4015::Unknown config param %d\n", param_id); + return false; + } +} +/***************************************************************************** + ** FUNCTION NAME : _probe + ** + ** DESCRIPTION : ocmp wrapper for handling POST(power on self test) + ** + ** ARGUMENTS : driver , postData + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _probe(void *driver, POSTData *postData) +{ + LTC4015_configure(driver); + return LTC4015_probe(driver, postData); +} + +static void _alert_handler(LTC4015_Event evt, int16_t value, void *alert_data) +{ + unsigned int alert; + switch (evt) { + case LTC4015_EVT_BVL: + alert = LTC4015_ALERT_BATTERY_VOLTAGE_LOW; + break; + case LTC4015_EVT_BVH: + alert = LTC4015_ALERT_BATTERY_VOLTAGE_HIGH; + break; + case LTC4015_EVT_BCL: + alert = LTC4015_ALERT_BATTERY_CURRENT_LOW; + break; + case LTC4015_EVT_IVL: + alert = LTC4015_ALERT_INPUT_VOLTAGE_LOW; + break; + case LTC4015_EVT_ICH: + alert = LTC4015_ALERT_INPUT_CURRENT_HIGH; + break; + case LTC4015_EVT_DTH: + alert = LTC4015_ALERT_DIE_TEMPERATURE_HIGH; + break; + default: + LOGGER_ERROR("Unknown LTC4015 evt: %d\n", evt); + return; + } + + OCMP_GenerateAlert(alert_data, alert, &value); + LOGGER_DEBUG("LTC4015 Event: %d Value: %d\n", evt, value); +} + +static ePostCode _init(void *driver, const void *config, + const void *alert_token) { + // if(!_choose_battery_charger(driver)) + // return false; + + if (LTC4015_init(driver) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + + if (!config) { + return POST_DEV_CFG_DONE; + } + + const LTC4015_Config *ltc4015_config = config; + + if (LTC4015_cfg_battery_voltage_low(driver, + ltc4015_config->batteryVoltageLow) + != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + if (LTC4015_cfg_battery_voltage_high(driver, + ltc4015_config->batteryVoltageHigh) + != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + if (LTC4015_cfg_battery_current_low(driver, + ltc4015_config->batteryCurrentLow) + != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + if (LTC4015_cfg_input_voltage_low(driver, ltc4015_config->inputVoltageLow) + != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + if (LTC4015_cfg_input_current_high(driver, ltc4015_config->inputCurrentHigh) + != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + if (LTC4015_cfg_input_current_limit(driver, + ltc4015_config->inputCurrentLimit) + != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + if (ltc4015_config->icharge) { + if (LTC4015_cfg_icharge(driver, ltc4015_config->icharge) + != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + } + if (ltc4015_config->vcharge) { + if (LTC4015_cfg_vcharge(driver, ltc4015_config->vcharge) + != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + } + + LTC4015_setAlertHandler(driver, _alert_handler, (void *)alert_token); + if (LTC4015_enableLimitAlerts(driver, + LTC4015_EVT_BVL | LTC4015_EVT_BVH + | LTC4015_EVT_IVL | LTC4015_EVT_ICH + | LTC4015_EVT_BCL) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + + if (LTC4015_enableChargerStateAlerts(driver, LTC4015_EVT_BMFA)) { + return POST_DEV_CFG_FAIL; + } + + return POST_DEV_CFG_DONE; +} + +const Driver_fxnTable LTC4015_fxnTable = { + /* Message handlers */ + .cb_probe = _probe, + .cb_init = _init, + .cb_get_status = _get_status, + .cb_get_config = _get_config, + .cb_set_config = _set_config, +}; diff --git a/firmware/psu/src/devices/ocmp_wrappers/ocmp_ltc4274.c b/firmware/psu/src/devices/ocmp_wrappers/ocmp_ltc4274.c new file mode 100644 index 0000000000..45010077ce --- /dev/null +++ b/firmware/psu/src/devices/ocmp_wrappers/ocmp_ltc4274.c @@ -0,0 +1,340 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "common/inc/ocmp_wrappers/ocmp_ltc4274.h" +#include "helpers/array.h" +#include "helpers/math.h" +#include "inc/devices/ltc4274.h" + +typedef enum LTC7274Status { + LTC7274_STATUS_DETECT = 0, + LTC7274_STATUS_CLASS , + LTC7274_STATUS_POWERGOOD, +} LTC7274Status; + +typedef enum LTC7274Config { + LTC4274_CONFIG_OPERATING_MODE = 0, + LTC4274_CONFIG_DETECT_ENABLE, + LTC4274_CONFIG_INTERRUPT_MASK, + LTC4274_CONFIG_INTERRUPT_ENABLE, + LTC4274_CONFIG_HP_ENABLE +} LTC7274Config; + +typedef enum LTC7274Alert { + LTC4274_ALERT_NO_ACTIVE = 0, + LTC4274_ALERT_POWER_ENABLE, + LTC4274_ALERT_POWERGOOD, + LTC4274_ALERT_DISCONNECT, + LTC4274_ALERT_DETECTION , + LTC4274_ALERT_CLASS, + LTC4274_ALERT_TCUT, + LTC4274_ALERT_TSTART, + LTC4274_ALERT_SUPPLY +} LTC7274Alert; + +bool LTC4274_reset(void *driver, void *params) +{ + ReturnStatus status = RETURN_OK; + status = ltc4274_reset(); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : _get_status + ** + ** DESCRIPTION : ocmp wrapper for getting status + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _get_status(void *driver, unsigned int param_id, + void *return_buf) { + bool ret = true; + uint8_t *res = return_buf; + switch (param_id) { + case LTC7274_STATUS_DETECT: + { + if (ltc4274_get_detection_status(driver, res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: Reading PSE detection and classification failed.\n"); + } + break; + } + case LTC7274_STATUS_CLASS: + { + if (ltc4274_get_class_status(driver, res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: Reading PSE power status failed.\n"); + } + break; + } + case LTC7274_STATUS_POWERGOOD: + { + if (ltc4274_get_powergood_status(driver, res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: Reading PSE power status failed.\n"); + return ret; + } + break; + } + default: + { + LOGGER("LTC4274:ERROR::Unkown parameter recived for PSE status.\n"); + ret = false; + } + } + return ret; +} + +/***************************************************************************** + ** FUNCTION NAME : _set_config + ** + ** DESCRIPTION : ocmp wrapper for setting config parameter + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _set_config(void *driver, unsigned int param_id, + const void *data) { + bool ret = true; + uint8_t *res = (uint8_t*)data; + switch (param_id) { + case LTC4274_CONFIG_OPERATING_MODE: + { + if ( ltc4274_set_cfg_operation_mode(driver, *res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: PSE operational mode setting mode failed.\n"); + } + break; + } + case LTC4274_CONFIG_DETECT_ENABLE: + { + if (ltc4274_set_cfg_detect_enable(driver, *res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: PSE detection and classification enable failed.\n"); + } + break; + } + case LTC4274_CONFIG_INTERRUPT_MASK: + { + if (ltc4274_set_interrupt_mask(driver, *res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR::PSE interrupts mask enable failed.\n"); + } + break; + } + case LTC4274_CONFIG_INTERRUPT_ENABLE: + { + if (ltc4274_cfg_interrupt_enable(driver, *res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: PSE interrupt enable failed.\n"); + } + break; + } + case LTC4274_CONFIG_HP_ENABLE: + { + if (ltc4274_set_cfg_pshp_feature(driver, *res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: PSE configuration for LTEPOE++ failed.\n"); + } + break; + } + default: + { + LOGGER("LTC4274:ERROR:: PSE configurion unkown parmeter passed.\n"); + ret = false; + } + } + return ret; +} + +/***************************************************************************** + ** FUNCTION NAME : _get_config + ** + ** DESCRIPTION : ocmp wrapper for getting config parameter + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _get_config(void *driver, unsigned int param_id, + void *return_buf) { + bool ret = true; + uint8_t *res = return_buf; + switch (param_id) { + case LTC4274_CONFIG_OPERATING_MODE: + { + if (ltc4274_get_operation_mode(driver, res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: PSE operational mode setting mode failed.\n"); + } + break; + } + case LTC4274_CONFIG_DETECT_ENABLE: + { + if (ltc4274_get_detect_enable(driver, res)!= RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: PSE detection and classification enable failed.\n"); + } + break; + } + case LTC4274_CONFIG_INTERRUPT_MASK: + { + if (ltc4274_get_interrupt_mask(driver, res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR::PSE interrupts mask enable failed.\n"); + } + break; + } + case LTC4274_CONFIG_INTERRUPT_ENABLE: + { + if (ltc4274_get_interrupt_enable(driver, res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: PSE interrupt enable failed.\n"); + } + break; + } + case LTC4274_CONFIG_HP_ENABLE: + { + if (ltc4274_get_pshp_feature(driver, res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: PSE configuration for LTEPOE++ failed.\n"); + } + break; + } + default: + { + LOGGER("LTC4274:ERROR:: PSE configurion unkown parmeter passed.\n"); + ret = false; + } + } + return ret; +} + +/***************************************************************************** + ** FUNCTION NAME : _probe + ** + ** DESCRIPTION : ocmp wrapper for handling POST(power on self test) + ** + ** ARGUMENTS : driver , postData + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _probe(void *driver, POSTData *postData) +{ + ltc4274_config(driver); + return ltc4274_probe(driver, postData); + +} + +/***************************************************************************** + ** FUNCTION NAME : _alert_handler + ** + ** DESCRIPTION : call back function for handling alerts from device + ** layer + ** + ** ARGUMENTS : event type , current value, alert config, + ** + ** RETURN TYPE : + ** + *****************************************************************************/ +static void _alert_handler(LTC4274_Event evt, + void *context) +{ + unsigned int alert; + switch(evt) { + case LTC4274_EVT_SUPPLY: + alert = LTC4274_ALERT_SUPPLY; + break; + case LTC4274_EVT_TSTART: + alert = LTC4274_ALERT_TSTART; + break; + case LTC4274_EVT_TCUT: + alert = LTC4274_ALERT_TCUT; + break; + case LTC4274_EVT_CLASS: + alert = LTC4274_ALERT_CLASS; + break; + case LTC4274_EVT_DETECTION: + alert = LTC4274_ALERT_DETECTION; + break; + case LTC4274_EVT_DISCONNECT: + alert = LTC4274_ALERT_DISCONNECT; + break; + case LTC4274_EVT_POWERGOOD: + alert = LTC4274_ALERT_POWERGOOD; + break; + case LTC4274_EVT_POWER_ENABLE: + alert = LTC4274_ALERT_POWER_ENABLE; + break; + default: + { + alert = LTC4274_ALERT_NO_ACTIVE; + return; + } + } + uint8_t alert_data = 0x00; + OCMP_GenerateAlert(context, alert, &alert_data); + LOGGER_DEBUG("LTC7274 Event: %d \n", evt); +} + +/***************************************************************************** + ** FUNCTION NAME : _init + ** + ** DESCRIPTION : ocmp wrapper for handling init + ** + ** ARGUMENTS : driver , driver config, context for cb function + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _init(void *driver, const void *config, + const void *alert_token) +{ + ltc4274_init(driver); + + if (!config) { + return POST_DEV_CFG_DONE; + } + const LTC4274_Config *LTC7274_config = config; + if ( ltc4274_set_cfg_operation_mode(driver,LTC7274_config->operatingMode) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + if ( ltc4274_set_cfg_detect_enable(driver,LTC7274_config->detectEnable) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + if ( ltc4274_set_interrupt_mask(driver,LTC7274_config->interruptMask) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + if ( ltc4274_set_cfg_pshp_feature(driver,LTC7274_config->pseHpEnable) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + ltc4274_set_alert_handler(driver, _alert_handler, (void *)alert_token); + //TODO: SET enable or disable. + if (ltc4274_cfg_interrupt_enable(driver, LTC7274_config->interruptEnable) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + ltc4274_update_stateInfo(driver); + + return POST_DEV_CFG_DONE; +} + +const Driver_fxnTable LTC4274_fxnTable = { + /* Message handlers */ + .cb_probe = _probe, + .cb_init = _init, + .cb_get_status = _get_status, + .cb_get_config = _get_config, + .cb_set_config = _set_config, +}; diff --git a/firmware/psu/src/devices/ocmp_wrappers/ocmp_ltc4295.c b/firmware/psu/src/devices/ocmp_wrappers/ocmp_ltc4295.c new file mode 100644 index 0000000000..025ac79ef6 --- /dev/null +++ b/firmware/psu/src/devices/ocmp_wrappers/ocmp_ltc4295.c @@ -0,0 +1,128 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "common/inc/ocmp_wrappers/ocmp_ltc4295.h" +#include "helpers/array.h" +#include "helpers/math.h" +#include "inc/devices/ltc4295.h" + +/***************************************************************************** + ** FUNCTION NAME : _get_status + ** + ** DESCRIPTION : ocmp wrapper for getting status + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _get_status(void *driver, unsigned int param_id, + void *return_buf) +{ + bool ret = false; + switch (param_id) { + case LTC4295_STATUS_CLASS: + { + ePDClassType *res = (ePDClassType*)return_buf; + if (ltc4295_get_class(driver, res) == RETURN_OK) { + ret = true; + } + } + break; + case LTC4295_STATUS_POWERGOOD: + { + ePDPowerState *res =(ePDPowerState*) return_buf; + if (ltc4295_get_power_good(driver, res) == RETURN_OK) { + ret = true; + } + break; + } + default: + { + LOGGER_ERROR("LTC4295::Unknown status param %d\n", param_id); + ret = false; + } + } + return ret; +} + +/***************************************************************************** + ** FUNCTION NAME : _probe + ** + ** DESCRIPTION : ocmp wrapper for handling POST(power on self test) + ** + ** ARGUMENTS : driver , postData + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _probe(void *driver, POSTData *postData) +{ + ltc4295_config(driver); + return ltc4295_probe(driver,postData); +} + +/***************************************************************************** + ** FUNCTION NAME : _alert_handler + ** + ** DESCRIPTION : call back function for handling alerts from device + ** layer + ** + ** ARGUMENTS : event type , current value, alert config, + ** + ** RETURN TYPE : + ** + *****************************************************************************/ +static void _alert_handler(LTC4295_Event evt, void *context) +{ + unsigned int alert; + switch (evt) { + case LTC4295_CONNECT_EVT: + alert = LTC4295_CONNECT_ALERT; + break; + case LTC4295_DISCONNECT_EVT: + alert = LTC4295_DISCONNECT_ALERT; + break; + case LTC4295_INCOMPATIBLE_EVT: + alert = LTC4295_INCOMPATIBLE_ALERT; + break; + default: + LOGGER_ERROR("Unknown LTC4295evt: %d\n", evt); + return; + } + OCMP_GenerateAlert(context, alert, &evt); + LOGGER_DEBUG("LTC4295A alert: %d generated.\n", alert); +} + +/***************************************************************************** + ** FUNCTION NAME : _init + ** + ** DESCRIPTION : ocmp wrapper for handling init + ** + ** ARGUMENTS : driver , driver config, context for cb function + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _init(void *driver, const void *config, + const void *alert_token) +{ + if (ltc4295_init(driver) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + + ltc4295_set_alert_handler(driver, _alert_handler, (void *)alert_token); + return POST_DEV_CFG_DONE; +} + +const Driver_fxnTable LTC4295_fxnTable = { + /* Message handlers */ + .cb_probe = _probe, + .cb_init = _init, + .cb_get_status = _get_status, +}; diff --git a/firmware/psu/src/devices/ocmp_wrappers/ocmp_powerSource.c b/firmware/psu/src/devices/ocmp_wrappers/ocmp_powerSource.c new file mode 100644 index 0000000000..6267b5cda1 --- /dev/null +++ b/firmware/psu/src/devices/ocmp_wrappers/ocmp_powerSource.c @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "common/inc/global/Framework.h" +#include "common/inc/ocmp_wrappers/ocmp_powersource.h" +#include "helpers/array.h" +#include "inc/devices/powerSource.h" + +/***************************************************************************** + ** FUNCTION NAME : _get_status + ** + ** DESCRIPTION : ocmp wrapper for getting status + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _get_status(void *driver, unsigned int param_id, + void *return_buf) +{ + bool ret = false; + pwr_get_source_info(driver); + if ( pwr_process_get_status_parameters_data(param_id,return_buf) == RETURN_OK) { + ret = true; + } + return ret; +} + +/***************************************************************************** + ** FUNCTION NAME : _probe + ** + ** DESCRIPTION : ocmp wrapper for handling POST(power on self test) + ** + ** ARGUMENTS : driver , postData + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _probe(void *driver) +{ + /* powersource is part of TIVA */ + return POST_DEV_FOUND; +} + +/***************************************************************************** + ** FUNCTION NAME : _init + ** + ** DESCRIPTION : ocmp wrapper for handling init + ** + ** ARGUMENTS : driver , driver config, context for cb function + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _init(void *driver, const void *config, + const void *alert_token) +{ + pwr_source_init(); + return POST_DEV_NO_CFG_REQ; +} + +const Driver_fxnTable PWRSRC_fxnTable = { + /* Message handlers */ + .cb_probe = _probe, + .cb_init = _init, + .cb_get_status = _get_status, +}; diff --git a/firmware/psu/src/devices/ocmp_wrappers/ocmp_se98a.c b/firmware/psu/src/devices/ocmp_wrappers/ocmp_se98a.c new file mode 100644 index 0000000000..bb7a13529b --- /dev/null +++ b/firmware/psu/src/devices/ocmp_wrappers/ocmp_se98a.c @@ -0,0 +1,207 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "common/inc/ocmp_wrappers/ocmp_se98a.h" +#include "helpers/array.h" +#include "helpers/math.h" +#include "inc/devices/se98a.h" + +typedef enum Se98aStatus { + SE98A_STATUS_TEMPERATURE = 0, +} Se98aStatus; + +typedef enum Se98aConfig { + SE98A_CONFIG_LIM_LOW = 0, + SE98A_CONFIG_LIM_HIGH, + SE98A_CONFIG_LIM_CRIT, +} Se98aConfig; + +typedef enum Se98aAlert { + SE98A_ALERT_LOW = 0, + SE98A_ALERT_HIGH, + SE98A_ALERT_CRITICAL +} Se98aAlert; + +/***************************************************************************** + ** FUNCTION NAME : _get_status + ** + ** DESCRIPTION : ocmp wrapper for getting status + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _get_status(void *driver, unsigned int param_id, + void *return_buf) { + switch (param_id) { + case SE98A_STATUS_TEMPERATURE: { + int8_t *res = return_buf; + if (se98a_read(driver, res) == RETURN_OK) { + return true; + } + break; + } + default: + LOGGER_ERROR("SE98A::Unknown status param %d\n", param_id); + return false; + } + return false; +} + +/***************************************************************************** + ** FUNCTION NAME : _get_config + ** + ** DESCRIPTION : ocmp wrapper for getting config parameter + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _get_config(void *driver, unsigned int param_id, + void *return_buf) { + switch (param_id) { + case SE98A_CONFIG_LIM_LOW: + case SE98A_CONFIG_LIM_HIGH: + case SE98A_CONFIG_LIM_CRIT: { + int8_t *res = return_buf; + if (se98a_get_limit(driver, param_id + 1, res) == RETURN_OK) { + return true; + } + break; + } + default: + LOGGER_ERROR("SE98A::Unknown config param %d\n", param_id); + return false; + } + return false; +} + +/***************************************************************************** + ** FUNCTION NAME : _set_config + ** + ** DESCRIPTION : ocmp wrapper for setting config parameter + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _set_config(void *driver, unsigned int param_id, + const void *data) { + switch (param_id) { + case SE98A_CONFIG_LIM_LOW: + case SE98A_CONFIG_LIM_HIGH: + case SE98A_CONFIG_LIM_CRIT: { + const int8_t *limit = data; + if (se98a_set_limit(driver, param_id + 1, *limit) == RETURN_OK) { + return true; + } + break; + } + default: + LOGGER_ERROR("SE98A::Unknown config param %d\n", param_id); + return false; + } + return false; +} + +/***************************************************************************** + ** FUNCTION NAME : _probe + ** + ** DESCRIPTION : ocmp wrapper for handling POST(power on self test) + ** + ** ARGUMENTS : driver , postData + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _probe(void *driver, POSTData *postData) +{ + return se98a_probe(driver, postData); +} + +/***************************************************************************** + ** FUNCTION NAME : _alert_handler + ** + ** DESCRIPTION : call back function for handling alerts from device + ** layer + ** + ** ARGUMENTS : event type , current value, alert config, + ** + ** RETURN TYPE : + ** + *****************************************************************************/ +static void _alert_handler(SE98A_Event evt, int8_t temperature, + void *context) +{ + unsigned int alert; + switch (evt) { + case SE98A_EVT_ACT: + alert = SE98A_ALERT_CRITICAL; + break; + case SE98A_EVT_AAW: + alert = SE98A_ALERT_HIGH; + break; + case SE98A_EVT_BAW: + alert = SE98A_ALERT_LOW; + break; + default: + LOGGER_ERROR("Unknown SE98A evt: %d\n", evt); + return; + } + + uint8_t alert_data = (uint8_t)MAX((int8_t)0, temperature); + OCMP_GenerateAlert(context, alert, &alert_data); + LOGGER_DEBUG("SE98A Event: %d Temperature: %d\n", evt, temperature); +} + +/***************************************************************************** + ** FUNCTION NAME : _init + ** + ** DESCRIPTION : ocmp wrapper for handling init + ** + ** ARGUMENTS : driver , driver config, context for cb function + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _init(void *driver, const void *config, + const void *alert_token) +{ + if (se98a_init(driver) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + if (!config) { + return POST_DEV_CFG_DONE; + } + const SE98A_Config *se98a_config = config; + for (int i = 0; i < ARRAY_SIZE(se98a_config->limits); ++i) { + if (se98a_set_limit(driver, i + 1, se98a_config->limits[i]) != + RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + } + + se98a_set_alert_handler(driver, _alert_handler, (void *)alert_token); + if (se98a_enable_alerts(driver) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + + return POST_DEV_CFG_DONE; +} + +const Driver_fxnTable SE98_fxnTable = { + /* Message handlers */ + .cb_probe = _probe, + .cb_init = _init, + .cb_get_status = _get_status, + .cb_get_config = _get_config, + .cb_set_config = _set_config, +}; diff --git a/firmware/psu/src/devices/powerSource.c b/firmware/psu/src/devices/powerSource.c new file mode 100644 index 0000000000..9164d8cfce --- /dev/null +++ b/firmware/psu/src/devices/powerSource.c @@ -0,0 +1,278 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** + +#include "Board.h" +#include "inc/common/global_header.h" +#include "inc/common/i2cbus.h" +#include "inc/devices/powerSource.h" +#include "inc/subsystem/power/power.h" + +#include +#include + +static tPowerSource Power_SourceInfo[PWR_SRC_MAX]; + +/***************************************************************************** + ** FUNCTION NAME : pwr_update_source_info + ** + ** DESCRIPTION : Update power source information. + ** + ** ARGUMENTS : Power Source and Power source state. + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void pwr_update_source_info(ePowerSource powerSrc, ePowerSourceState pwrState) +{ + + ePowerSource itr = PWR_SRC_EXT ; + for (; itr < PWR_SRC_MAX; itr++) { + if (Power_SourceInfo[itr].powerSource == powerSrc) { + Power_SourceInfo[itr].state = pwrState; + LOGGER("POWER:INFO:: Power State updated for Power Source %d with %d.\n", + Power_SourceInfo[itr].powerSource, + Power_SourceInfo[itr].state); + } + } +} + +/****************************************************************************** + * @fn pwr_check_poe + * + * @brief Check presence of POE. + * + * @args None. + * + * @return ReturnStatus + ******************************************************************************/ +static ReturnStatus pwr_check_poe(PWRSRC_Dev *pwrSrcDev) +{ + ReturnStatus ret = RETURN_NOTOK; + uint8_t value = 0; + ePowerSourceState status = PWR_SRC_NON_AVAILABLE; + //For Checking POE POWER SOURCE + value = OcGpio_read(&pwrSrcDev->cfg.pin_poe_prsnt_n); + if ( value == 0) { + status=PWR_SRC_AVAILABLE; + ret = RETURN_OK; + } + pwr_update_source_info(PWR_SRC_POE, status); + return ret; +} + +/****************************************************************************** + * @fn pwr_check_ext_power + * + * @brief Check presence of external power. + * + * @args None. + * + * @return ReturnStatus + ******************************************************************************/ +static ReturnStatus pwr_check_ext_power(PWRSRC_Dev *pwrSrcDev) +{ + ReturnStatus ret = RETURN_NOTOK; + uint8_t value = 0; + ePowerSourceState status = PWR_SRC_NON_AVAILABLE; + //For Checking POE POWER SOURCE + value = OcGpio_read(&pwrSrcDev->cfg.pin_dc_present); + if ( value == 0) { + status=PWR_SRC_AVAILABLE; + ret = RETURN_OK; + } + pwr_update_source_info(PWR_SRC_EXT, status); + return ret; +} + +/****************************************************************************** + * @fn pwr_check_batt + * + * @brief Check presence of Battery. + * + * @args None. + * + * @return ReturnStatus + ******************************************************************************/ +static ReturnStatus pwr_check_batt(PWRSRC_Dev *pwrSrcDev) +{ + ReturnStatus ret = RETURN_NOTOK; + uint8_t value = 0; + ePowerSourceState status = PWR_SRC_NON_AVAILABLE; + //For Checking POE POWER SOURCE + value = OcGpio_read(&pwrSrcDev->cfg.pin_int_bat_prsnt); + if ( value == 0) { + status=PWR_SRC_AVAILABLE; + ret = RETURN_OK; + } + pwr_update_source_info(PWR_SRC_LIION_BATT, status); + return ret; +} + +/****************************************************************************** + ** FUNCTION NAME : pwr_check_presence_of_source + ** + ** DESCRIPTION : check for power source available. + ** + ** ARGUMENTS : pointer to powersource config + ** + ** RETURN TYPE : None + ** + ******************************************************************************/ +static void pwr_check_presence_of_source(PWRSRC_Dev *pwrSrcDev) +{ + ReturnStatus ret = RETURN_NOTOK; + ret = pwr_check_ext_power(pwrSrcDev); + LOGGER("POWER:INFO:: Power Source External %s.\n", + ((ret == RETURN_OK) ? "available" : "not available")); + + ret = pwr_check_poe(pwrSrcDev); + LOGGER("POWER:INFO:: Power Source POE %s.\n", + ((ret == RETURN_OK) ? "available" : "not available")); + + ret = pwr_check_batt(pwrSrcDev); + LOGGER("POWER:INFO:: Power Source BATTERY %s.\n", + ((ret == RETURN_OK) ? "available" : "not available")); + + return ; +} + +/****************************************************************************** + ** FUNCTION NAME : pwr_source_inuse + ** + ** DESCRIPTION : Give info about currently used power source. + ** + ** ARGUMENTS : output pointer for storing powersource + ** + ** RETURN TYPE : RETURN_OK or RETURN_NOTOK + ** + ******************************************************************************/ +static ReturnStatus pwr_source_inuse(ePowerSource *inUse) +{ + ReturnStatus ret = RETURN_NOTOK; + ePowerSource itr = PWR_SRC_EXT ; + for ( ; itr < PWR_SRC_MAX; itr++) { + if (Power_SourceInfo[itr].state == PWR_SRC_AVAILABLE) { + *inUse = itr; + ret = RETURN_OK; + break; + } + + } + return ret; +} + +/***************************************************************************** + ** FUNCTION NAME : pwr_source_init + ** + ** DESCRIPTION : initialize power source information. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void pwr_source_init() +{ + ePowerSource itr = PWR_SRC_EXT ; + for (; itr < PWR_SRC_MAX; itr++) { + Power_SourceInfo[itr].powerSource = itr; + Power_SourceInfo[itr].state = PWR_SRC_NON_AVAILABLE; + } + + return; +} + +/***************************************************************************** + ** FUNCTION NAME : pwr_get_source_info + ** + ** DESCRIPTION : initialize power source information. + ** + ** ARGUMENTS : power source config + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void pwr_get_source_info(PWRSRC_Dev *pwrSrcDev) +{ + ReturnStatus status = RETURN_NOTOK; + ePowerSource powerSource = PWR_SRC_EXT; + /* Check the presence of power sources*/ + pwr_check_presence_of_source(pwrSrcDev); + + /* Find the primary power source and update Power Source info for same.*/ + status = pwr_source_inuse(&powerSource); + if (status != RETURN_OK) { + LOGGER("POWER:ERROR:: Failed to get current power source.\n"); + } else { + LOGGER("POWER:INFO:: Current Power source is 0x%x.\n", powerSource); + pwr_update_source_info(powerSource, PWR_SRC_ACTIVE); + } +} + +/***************************************************************************** + ** FUNCTION NAME : pwr_process_get_status_parameters_data + ** + ** DESCRIPTION : Get Power Status Message. + ** + ** ARGUMENTS : parameter id ,Pointer to OCMPMessageFrame payload + ** + ** RETURN TYPE : RETURN_OK or RETURN_NOTOK + ** + *****************************************************************************/ +ReturnStatus +pwr_process_get_status_parameters_data(ePower_StatusParamId paramIndex, + uint8_t *pPowerStatusData) +{ + ReturnStatus status = RETURN_OK; + switch (paramIndex) { + case PWR_STAT_EXT_PWR_AVAILABILITY: { + if ((Power_SourceInfo[PWR_SRC_POE].state == PWR_SRC_ACTIVE) || + (Power_SourceInfo[PWR_SRC_POE].state == PWR_SRC_AVAILABLE)) + *pPowerStatusData = 1; + break; + } + case PWR_STAT_EXT_PWR_ACTIVE: { + if (Power_SourceInfo[PWR_SRC_POE].state == PWR_SRC_ACTIVE) + *pPowerStatusData = 1; + break; + } + case PWR_STAT_POE_AVAILABILITY: { + if ((Power_SourceInfo[PWR_SRC_POE].state == PWR_SRC_ACTIVE) || + (Power_SourceInfo[PWR_SRC_POE].state == PWR_SRC_AVAILABLE)) + *pPowerStatusData = 1; + break; + } + case PWR_STAT_POE_ACTIVE: { + if (Power_SourceInfo[PWR_SRC_POE].state == PWR_SRC_ACTIVE) + *pPowerStatusData = 1; + break; + } + case PWR_STAT_BATT_AVAILABILITY: { + if ((Power_SourceInfo[PWR_SRC_LIION_BATT].state == + PWR_SRC_ACTIVE) || + (Power_SourceInfo[PWR_SRC_LIION_BATT].state == + PWR_SRC_AVAILABLE)) + *pPowerStatusData = 1; + break; + } + case PWR_STAT_BATT_ACTIVE: { + if (Power_SourceInfo[PWR_SRC_LIION_BATT].state == PWR_SRC_ACTIVE) + *pPowerStatusData = 1; + break; + } + default: { + LOGGER("POWER::ERROR: Invalid Power param status.\n"); + } + } + return status; +} diff --git a/firmware/psu/src/devices/se98a.c b/firmware/psu/src/devices/se98a.c new file mode 100644 index 0000000000..9f42aebe9f --- /dev/null +++ b/firmware/psu/src/devices/se98a.c @@ -0,0 +1,405 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "devices/i2c/threaded_int.h" +#include "helpers/math.h" +#include "helpers/memory.h" +#include "inc/common/byteorder.h" +#include "inc/devices/se98a.h" + +#include +#include + +/***************************************************************************** + * Register Definitions + *****************************************************************************/ +/* Register Addresses */ +#define SE98A_REG_CAPS 0x00 +#define SE98A_REG_CFG 0x01 +#define SE98A_REG_HIGH_LIM 0x02 +#define SE98A_REG_LOW_LIM 0x03 +#define SE98A_REG_CRIT_LIM 0x04 +#define SE98A_REG_TEMP 0x05 +#define SE98A_REG_MFG_ID 0x06 +#define SE98A_REG_DEV_ID 0x07 + +/* Temperature Sensor Info */ +#define SE98A_MFG_ID 0x1131 +#define SE98A_DEV_ID 0xA1 + +/* Configuration Bits */ +#define SE98A_CFG_HEN_H (1 << 10) /* Hysteresis Enable High Bit */ +#define SE98A_CFG_HEN_L (1 << 9) /* Hysteresis Enable Low Bit */ +#define SE98A_CFG_SHMD (1 << 8) /* Shutdown Mode */ + +#define SE98A_CFG_CTLB (1 << 7) /* Critical Trip Lock Bit */ +#define SE98A_CFG_AWLB (1 << 6) /* Alarm Window Lock Bit */ +#define SE98A_CFG_CEVENT (1 << 5) /* (WO) Clear EVENT */ +#define SE98A_CFG_ESTAT (1 << 4) /* (RO) EVENT Status */ + +#define SE98A_CFG_EOCTL (1 << 3) /* EVENT Output Control */ +#define SE98A_CFG_CVO (1 << 2) /* Critical Event Only */ +#define SE98A_CFG_EP (1 << 1) /* EVENT Polarity */ +#define SE98A_CFG_EMD (1 << 0) /* EVENT Mode */ + +#define SE98A_CFG_HYS_0 (0x0 << 9) +#define SE98A_CFG_HYS_1P5 (0x1 << 9) +#define SE98A_CFG_HYS_3 (0x2 << 9) +#define SE98A_CFG_HYS_6 (0x3 << 9) + +/* Default CFG plus interrupt mode (we don't support comparator mode) */ +#define SE98A_CONFIG_DEFAULT (0x0000 | SE98A_CFG_EMD | SE98A_CFG_HYS_1P5) + +/***************************************************************************** + * Helper to read from a SE98A register + *****************************************************************************/ +static ReturnStatus se98a_reg_read(const SE98A_Dev *dev, + uint8_t regAddress, + uint16_t *regValue) +{ + ReturnStatus status = RETURN_NOTOK; + I2C_Handle tempHandle = i2c_get_handle(dev->cfg.dev.bus); + if (!tempHandle) { + LOGGER_ERROR("SE98A:ERROR:: Failed to get I2C Bus for Temperature " + "sensor 0x%x on bus 0x%x.\n", dev->cfg.dev.slave_addr, + dev->cfg.dev.bus); + } else { + status = i2c_reg_read(tempHandle, dev->cfg.dev.slave_addr, regAddress, + regValue, 2); + *regValue = betoh16(*regValue); + } + return status; +} + +/***************************************************************************** + * Helper to write to a SE98A register + *****************************************************************************/ +static ReturnStatus se98a_reg_write(const SE98A_Dev *dev, + uint8_t regAddress, + uint16_t regValue) +{ + ReturnStatus status = RETURN_NOTOK; + I2C_Handle tempHandle = i2c_get_handle(dev->cfg.dev.bus); + if (!tempHandle) { + LOGGER_ERROR("SE98A:ERROR:: Failed to get I2C Bus for Temperature " + "sensor 0x%x on bus 0x%x.\n", dev->cfg.dev.slave_addr, + dev->cfg.dev.bus); + } else { + regValue = htobe16(regValue); + status = i2c_reg_write(tempHandle, dev->cfg.dev.slave_addr, regAddress, + regValue, 2); + } + return status; +} + +/***************************************************************************** + * Helper to read the device ID + *****************************************************************************/ +static ReturnStatus se98a_get_dev_id(const SE98A_Dev *dev, uint8_t *devID) +{ + uint16_t regValue; + ReturnStatus status = se98a_reg_read(dev, SE98A_REG_DEV_ID, + ®Value); + if (status == RETURN_OK) { + /* Strip off the revision - we don't care about it right now */ + *devID = HIBYTE(regValue); + } + return status; +} + +/***************************************************************************** + * Helper to read the manufacturer ID + *****************************************************************************/ +static ReturnStatus se98a_get_mfg_id(const SE98A_Dev *dev, uint16_t *mfgID) +{ + return se98a_reg_read(dev, SE98A_REG_MFG_ID, mfgID); +} + +/***************************************************************************** + * Helper to write to the configuration register + *****************************************************************************/ +static ReturnStatus se98a_set_config_reg(const SE98A_Dev *dev, + uint16_t configValue) +{ + return se98a_reg_write(dev, SE98A_REG_CFG, configValue); +} + +/***************************************************************************** + *****************************************************************************/ +ReturnStatus se98a_set_limit(SE98A_Dev *dev, + eTempSensor_ConfigParamsId limitToConfig, + int8_t tempLimitValue) +{ + uint8_t regAddress = 0x00; + + /* Get the limit to configure */ + switch (limitToConfig) { + case CONF_TEMP_SE98A_LOW_LIMIT_REG: + regAddress = SE98A_REG_LOW_LIM; + break; + case CONF_TEMP_SE98A_HIGH_LIMIT_REG: + regAddress = SE98A_REG_HIGH_LIM; + break; + case CONF_TEMP_SE98A_CRITICAL_LIMIT_REG: + regAddress = SE98A_REG_CRIT_LIM; + break; + default: + return RETURN_NOTOK; + } + + /* + * [15..13] RFU + * [12] SIGN (2's complement) + * [11..4] Integer part (8 bits) + * [3..2] Fractional part (0.5, 0.25) + * [1..0] RFU + */ + + /* The device technically takes an int9, but is only rated from + * -40 to +125, so we'll settle for an int8 */ + uint16_t regValue = ((int16_t)tempLimitValue & 0x00FF) << 4; + + /* Set the sign bit if negative */ + if (tempLimitValue < 0) { + regValue |= 0x1000; + } + + return se98a_reg_write(dev, regAddress, regValue); +} + +/***************************************************************************** + * Helper to convert a SE98A register value to a temperature + *****************************************************************************/ +static int8_t reg2temp(uint16_t reg) { + /* The limit regs have lower precision, so by making this function common, + * we lose 0.125 precision... since we round to the nearest int, I'm not + * worried */ + + /* Calculate the Actual Temperature Value from fixed point register + * (bottom 4 bits are fractional part - divide by 16) + * + * Register map REG_TEMP / REG_LIM + * [15..13] Trip Status / RFU + * [12] Sign / Sign + * [11..5] 8-bit Int / 8-bit Int + * [4..2] Fraction / Fraction + * [1] Fraction / RFU + * [0] RFU + */ + int16_t temperature = (reg & 0x0FFC); + + /* If negative, upper bits must be set (assume a 2's comp. system) */ + if (reg & 0x1000) { + temperature |= 0xF000; + } + temperature = round(temperature / 16.0f); + + return CONSTRAIN(temperature, INT8_MIN, INT8_MAX); +} + +/***************************************************************************** + *****************************************************************************/ +ReturnStatus se98a_read(SE98A_Dev *dev, int8_t *tempValue) +{ + /* The temperature value is 2's complement with the LSB equal to 0.0625. + * The resolution is 0.125 (bit 0 is unused) + */ + uint16_t regValue = 0x0000; + ReturnStatus status = se98a_reg_read(dev, SE98A_REG_TEMP, ®Value); + if (status == RETURN_OK) { + *tempValue = reg2temp(regValue); + LOGGER_DEBUG("TEMPSENSOR:INFO:: Temperature sensor 0x%x on bus " + "0x%x is reporting Temperature value of %d Celsius.\n", + dev->cfg.dev.slave_addr, dev->cfg.dev.bus, *tempValue); + } + return status; +} + + +/***************************************************************************** + * Helper to read the configuration register + *****************************************************************************/ +static ReturnStatus se98a_get_config_reg(const SE98A_Dev *dev, + uint16_t *configValue) +{ + return se98a_reg_read(dev, SE98A_REG_CFG, configValue); +} + +/***************************************************************************** + *****************************************************************************/ +ReturnStatus se98a_get_limit(SE98A_Dev *dev, + eTempSensor_ConfigParamsId limitToConfig, + int8_t* tempLimitValue) +{ + ReturnStatus status = RETURN_NOTOK; + uint16_t regValue = 0x0000; + uint8_t regAddress = 0x0000; + + /*getting the limit to configure */ + switch (limitToConfig) { + case CONF_TEMP_SE98A_LOW_LIMIT_REG: + regAddress = SE98A_REG_LOW_LIM; + break; + case CONF_TEMP_SE98A_HIGH_LIMIT_REG: + regAddress = SE98A_REG_HIGH_LIM; + break; + case CONF_TEMP_SE98A_CRITICAL_LIMIT_REG: + regAddress = SE98A_REG_CRIT_LIM; + break; + default: + return RETURN_NOTOK; + } + + status = se98a_reg_read(dev, regAddress, ®Value); + if (status == RETURN_OK) { + *tempLimitValue = reg2temp(regValue); + LOGGER_DEBUG("TEMPSENSOR:INFO:: Temperature sensor 0x%x on bus " + "0x%x is having Limit configure to 0x%x.\n", + dev->cfg.dev.slave_addr, dev->cfg.dev.bus, *tempLimitValue); + } + return status; +} + +/***************************************************************************** + * Internal IRQ handler - reads in triggered interrupts and dispatches CBs + *****************************************************************************/ +static void se98a_handle_irq(void *context) { + SE98A_Dev *dev = context; + + ReturnStatus res = RETURN_NOTOK; + const IArg mutexKey = GateMutex_enter(dev->obj.mutex); { + /* See if this event was from us (we can't just read the trip status + * since those bits are sticky - they could be from an old event) */ + uint16_t config_reg; + if ((se98a_get_config_reg(dev, &config_reg) == RETURN_OK) && + (config_reg & SE98A_CFG_ESTAT)) { + /* Clear the event */ + config_reg |= SE98A_CFG_CEVENT; + res = se98a_set_config_reg(dev, config_reg); + } + } GateMutex_leave(dev->obj.mutex, mutexKey); + + if (res != RETURN_OK) { + return; + } + + /* Read the temperature register which also contains event status */ + uint16_t regValue; + if (se98a_reg_read(dev, SE98A_REG_TEMP, ®Value) != RETURN_OK) { + /* Something really strange happened */ + return; + } + + /* Grab the upper 3 bits which represent the event trip status */ + uint8_t trip_stat = (regValue >> 13) & 0x07; + int8_t temperature = reg2temp(regValue); + + /* See if we have a callback assigned to handle alerts */ + if (!dev->obj.alert_cb) { + return; + } + + /* Since > CRIT implies above window, we only handle the highest priority + * event to avoid duplicate events being sent */ + if (trip_stat & SE98A_EVT_ACT) { + dev->obj.alert_cb(SE98A_EVT_ACT, temperature, dev->obj.cb_context); + } else if (trip_stat & SE98A_EVT_AAW) { + dev->obj.alert_cb(SE98A_EVT_AAW, temperature, dev->obj.cb_context); + } else if (trip_stat & SE98A_EVT_BAW) { + dev->obj.alert_cb(SE98A_EVT_BAW, temperature, dev->obj.cb_context); + } +} + +/***************************************************************************** + *****************************************************************************/ +ReturnStatus se98a_init(SE98A_Dev *dev) +{ + dev->obj = (SE98A_Obj){}; + + dev->obj.mutex = GateMutex_create(NULL, NULL); + if (!dev->obj.mutex) { + return RETURN_NOTOK; + } + + /* Make sure we're talking to the right device */ + //if (se98a_probe(dev) != POST_DEV_FOUND) { + // return RETURN_NOTOK; + //} + + /* The only way to truly reset this device is to cycle power - we'll just + * clear out the config register to be safe and clear any interrupts from + * a previous life */ + if (se98a_set_config_reg( + dev, SE98A_CONFIG_DEFAULT | SE98A_CFG_CEVENT) != + RETURN_OK) { + return RETURN_NOTOK; + } + + if (dev->cfg.pin_evt) { + const uint32_t pin_evt_cfg = OCGPIO_CFG_INPUT | OCGPIO_CFG_INT_FALLING; + /*TODO:Temp*/ + if (OcGpio_configure(dev->cfg.pin_evt, pin_evt_cfg) < OCGPIO_SUCCESS) { + return RETURN_NOTOK; + } + + /* Use a threaded interrupt to handle IRQ */ + // ThreadedInt_Init(dev->cfg.pin_evt, se98a_handle_irq, (void *)dev); + } + return RETURN_OK; +} + +/***************************************************************************** + *****************************************************************************/ +void se98a_set_alert_handler(SE98A_Dev *dev, SE98A_CallbackFn alert_cb, + void *cb_context) +{ + dev->obj.alert_cb = alert_cb; + dev->obj.cb_context = cb_context; +} + +/***************************************************************************** + *****************************************************************************/ +ReturnStatus se98a_enable_alerts(SE98A_Dev *dev) +{ + /* Wait 125ms after setting alarm window before enabling event pin + * see datasheet page 8 - 7.3.2.1 Alarm window */ + Task_sleep(125); + + ReturnStatus res = RETURN_NOTOK; + const IArg mutexKey = GateMutex_enter(dev->obj.mutex); { + uint16_t config_reg; + if (se98a_get_config_reg(dev, &config_reg) == RETURN_OK) { + config_reg |= SE98A_CFG_EOCTL; + res = se98a_set_config_reg(dev, config_reg); + } + } GateMutex_leave(dev->obj.mutex, mutexKey); + return res; +} + +/***************************************************************************** + *****************************************************************************/ +ePostCode se98a_probe(SE98A_Dev *dev, POSTData *postData) +{ + uint8_t devId = 0x00; + uint16_t manfId = 0x0000; + if (se98a_get_dev_id(dev, &devId) != RETURN_OK) { + return POST_DEV_MISSING; + } + if (devId != SE98A_DEV_ID) { + return POST_DEV_ID_MISMATCH; + } + + if (se98a_get_mfg_id(dev, &manfId) != RETURN_OK) { + return POST_DEV_MISSING; + } + if (manfId != SE98A_MFG_ID) { + return POST_DEV_ID_MISMATCH; + } + post_update_POSTData(postData, dev->cfg.dev.bus, dev->cfg.dev.slave_addr,manfId, devId); + return POST_DEV_FOUND; +} diff --git a/firmware/psu/src/devices/sx1509.c b/firmware/psu/src/devices/sx1509.c new file mode 100644 index 0000000000..3746346b35 --- /dev/null +++ b/firmware/psu/src/devices/sx1509.c @@ -0,0 +1,832 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** +#include "inc/common/byteorder.h" +#include "inc/common/global_header.h" +#include "inc/devices/sx1509.h" + +/***************************************************************************** + * REGISTER DEFINITIONS + *****************************************************************************/ +#define SX1509_REG_INPUT_DISABLE_B 0x00 /* Input buffer disable register I/O[15..8] (Bank B) */ +#define SX1509_REG_INPUT_DISABLE_A 0x01 /* Input buffer disable register I/O[7..0] (Bank A) */ +#define SX1509_REG_LONG_SLEW_B 0x02 /* Output buffer long slew register I/O[15..8] (Bank B) */ +#define SX1509_REG_LONG_SLEW_A 0x03 /* Output buffer long slew register I/O[7..0] (Bank A) */ +#define SX1509_REG_LOW_DRIVE_B 0x04 /* Output buffer low drive register I/O[15..8] (Bank B) */ +#define SX1509_REG_LOW_DRIVE_A 0x05 /* Output buffer low drive register I/O[7..0] (Bank A) */ +#define SX1509_REG_PULL_UP_B 0x06 /* Pull_up register I/O[15..8] (Bank B) */ +#define SX1509_REG_PULL_UP_A 0x07 /* Pull_up register I/O[7..0] (Bank A) */ +#define SX1509_REG_PULL_DOWN_B 0x08 /* Pull_down register I/O[15..8] (Bank B) */ +#define SX1509_REG_PULL_DOWN_A 0x09 /* Pull_down register I/O[7..0] (Bank A) */ +#define SX1509_REG_OPEN_DRAIN_B 0x0A /* Open drain register I/O[15..8] (Bank B) */ +#define SX1509_REG_OPEN_DRAIN_A 0x0B /* Open drain register I/O[7..0] (Bank A) */ +#define SX1509_REG_POLARITY_B 0x0C /* Polarity register I/O[15..8] (Bank B) */ +#define SX1509_REG_POLARITY_A 0x0D /* Polarity register I/O[7..0] (Bank A) */ +#define SX1509_REG_DIR_B 0x0E /* Direction register I/O[15..8] (Bank B) */ +#define SX1509_REG_DIR_A 0x0F /* Direction register I/O[7..0] (Bank A) */ +#define SX1509_REG_DATA_B 0x10 /* Data register I/O[15..8] (Bank B) */ +#define SX1509_REG_DATA_A 0x11 /* Data register I/O[7..0] (Bank A) */ +#define SX1509_REG_INTERRUPT_MASK_B 0x12 /* Interrupt mask register I/O[15..8] (Bank B) */ +#define SX1509_REG_INTERRUPT_MASK_A 0x13 /* Interrupt mask register I/O[7..0] (Bank A) */ +#define SX1509_REG_SENSE_HIGH_B 0x14 /* Sense register for I/O[15:12] (Bank B) */ +#define SX1509_REG_SENSE_LOW_B 0x15 /* Sense register for I/O[11:8] (Bank B) */ +#define SX1509_REG_SENSE_HIGH_A 0x16 /* Sense register for I/O[7:4] (Bank A) */ +#define SX1509_REG_SENSE_LOW_A 0x17 /* Sense register for I/O[3:0] (Bank A) */ +#define SX1509_REG_INTERRUPT_SOURCE_B 0x18 /* Interrupt source register I/O[15..8] (Bank B) */ +#define SX1509_REG_INTERRUPT_SOURCE_A 0x19 /* Interrupt source register I/O[7..0] (Bank A) */ +#define SX1509_REG_EVENT_STATUS_B 0x1A /* Event status register I/O[15..8] (Bank B) */ +#define SX1509_REG_EVENT_STATUS_A 0x1B /* Event status register I/O[7..0] (Bank A) */ +#define SX1509_REG_LEVEL_SHIFTER_1 0x1C /* Level shifter register 1 */ +#define SX1509_REG_LEVEL_SHIFTER_2 0x1D /* Level shifter register 2 */ +#define SX1509_REG_CLOCK 0x1E /* Clock management register */ +#define SX1509_REG_MISC 0x1F /* Miscellaneous device settings register */ +#define SX1509_REG_LED_DRIVER_ENABLE_B 0x20 /* LED driver enable register I/O[15..8] (Bank B) */ +#define SX1509_REG_LED_DRIVER_ENABLE_A 0x21 /* LED driver enable register I/O[7..0] (Bank A) */ +#define SX1509_REG_DEBOUNCE_CONFIG 0x22 /* Debounce configuration register */ +#define SX1509_REG_DEBOUNCE_ENABLE_B 0x23 /* Debounce enable register I/O[15..8] (Bank B) */ +#define SX1509_REG_DEBOUNCE_ENABLE_A 0x24 /* Debounce enable register I/O[7..0] (Bank A) */ +#define SX1509_REG_T_ON_0 0x29 /* ON time register for I/O[0] */ +#define SX1509_REG_I_ON_0 0x2A /* ON intensity register for I/O[0] */ +#define SX1509_REG_OFF_0 0x2B /* OFF time/intensity register for I/O[0] */ +#define SX1509_REG_T_ON_1 0x2C /* ON time register for I/O[1] */ +#define SX1509_REG_I_ON_1 0x2D /* ON intensity register for I/O[1] */ +#define SX1509_REG_OFF_1 0x2E /* OFF time/intensity register for I/O[1] */ +#define SX1509_REG_T_ON_2 0x2F /* ON time register for I/O[2] */ +#define SX1509_REG_I_ON_2 0x30 /* ON intensity register for I/O[2] */ +#define SX1509_REG_OFF_2 0x31 /* OFF time/intensity register for I/O[2] */ +#define SX1509_REG_T_ON_3 0x32 /* ON time register for I/O[3] */ +#define SX1509_REG_I_ON_3 0x33 /* ON intensity register for I/O[3] */ +#define SX1509_REG_OFF_3 0x34 /* OFF time/intensity register for I/O[3] */ +#define SX1509_REG_T_ON_4 0x35 /* ON time register for I/O[4] */ +#define SX1509_REG_I_ON_4 0x36 /* ON intensity register for I/O[4] */ +#define SX1509_REG_OFF_4 0x37 /* OFF time/intensity register for I/O[4] */ +#define SX1509_REG_T_RISE_4 0x38 /* Fade in register for I/O[4] */ +#define SX1509_REG_T_FALL_4 0x39 /* Fade out register for I/O[4] */ +#define SX1509_REG_T_ON_5 0x3A /* ON time register for I/O[5] */ +#define SX1509_REG_I_ON_5 0x3B /* ON intensity register for I/O[5] */ +#define SX1509_REG_OFF_5 0x3C /* OFF time/intensity register for I/O[5] */ +#define SX1509_REG_T_RISE_5 0x3D /* Fade in register for I/O[5] */ +#define SX1509_REG_T_FALL_5 0x3E /* Fade out register for I/O[5] */ +#define SX1509_REG_T_ON_6 0x3F /* ON time register for I/O[6] */ +#define SX1509_REG_I_ON_6 0x40 /* ON intensity register for I/O[6] */ +#define SX1509_REG_OFF_6 0x41 /* OFF time/intensity register for I/O[6] */ +#define SX1509_REG_T_RISE_6 0x42 /* Fade in register for I/O[6] */ +#define SX1509_REG_T_FALL_6 0x43 /* Fade out register for I/O[6] */ +#define SX1509_REG_T_ON_7 0x44 /* ON time register for I/O[7] */ +#define SX1509_REG_I_ON_7 0x45 /* ON intensity register for I/O[7] */ +#define SX1509_REG_OFF_7 0x46 /* OFF time/intensity register for I/O[7] */ +#define SX1509_REG_T_RISE_7 0x47 /* Fade in register for I/O[7] */ +#define SX1509_REG_T_FALL_7 0x48 /* Fade out register for I/O[7] */ +#define SX1509_REG_T_ON_8 0x49 /* ON time register for I/O[8] */ +#define SX1509_REG_I_ON_8 0x4A /* ON intensity register for I/O[8] */ +#define SX1509_REG_OFF_8 0x4B /* OFF time/intensity register for I/O[8] */ +#define SX1509_REG_T_ON_9 0x4C /* ON time register for I/O[9] */ +#define SX1509_REG_I_ON_9 0x4D /* ON intensity register for I/O[9] */ +#define SX1509_REG_OFF_9 0x4E /* OFF time/intensity register for I/O[9] */ +#define SX1509_REG_T_ON_10 0x4F /* ON time register for I/O[10] */ +#define SX1509_REG_I_ON_10 0x50 /* ON intensity register for I/O[10] */ +#define SX1509_REG_OFF_10 0x51 /* OFF time/intensity register for I/O[10] */ +#define SX1509_REG_T_ON_11 0x52 /* ON time register for I/O[11] */ +#define SX1509_REG_I_ON_11 0x53 /* ON intensity register for I/O[11] */ +#define SX1509_REG_OFF_11 0x54 /* OFF time/intensity register for I/O[11] */ +#define SX1509_REG_T_ON_12 0x55 /* ON time register for I/O[12] */ +#define SX1509_REG_I_ON_12 0x56 /* ON intensity register for I/O[12] */ +#define SX1509_REG_OFF_12 0x57 /* OFF time/intensity register for I/O[12] */ +#define SX1509_REG_T_RISE_12 0x58 /* Fade in register for I/O[12] */ +#define SX1509_REG_T_FALL_12 0x59 /* Fade out register for I/O[12] */ +#define SX1509_REG_T_ON_13 0x5A /* ON time register for I/O[13] */ +#define SX1509_REG_I_ON_13 0x5B /* ON intensity register for I/O[13] */ +#define SX1509_REG_OFF_13 0x5C /* OFF time/intensity register for I/O[13] */ +#define SX1509_REG_T_RISE_13 0x5D /* Fade in register for I/O[13] */ +#define SX1509_REG_T_FALL_13 0x5E /* Fade out register for I/O[13] */ +#define SX1509_REG_T_ON_14 0x5F /* ON time register for I/O[14] */ +#define SX1509_REG_I_ON_14 0x60 /* ON intensity register for I/O[14] */ +#define SX1509_REG_OFF_14 0x61 /* OFF time/intensity register for I/O[14] */ +#define SX1509_REG_T_RISE_14 0x62 /* Fade in register for I/O[14] */ +#define SX1509_REG_T_FALL_14 0x63 /* Fade out register for I/O[14] */ +#define SX1509_REG_T_ON_15 0x64 /* ON time register for I/O[15] */ +#define SX1509_REG_I_ON_15 0x65 /* ON intensity register for I/O[15] */ +#define SX1509_REG_OFF_15 0x66 /* OFF time/intensity register for I/O[15] */ +#define SX1509_REG_T_RISE_15 0x67 /* Fade in register for I/O[15] */ +#define SX1509_REG_T_FALL_15 0x68 /* Fade out register for I/O[15] */ +#define SX1509_REG_HIGH_INPUT_B 0x69 /* High input enable register I/O[15..8] (Bank B) */ +#define SX1509_REG_HIGH_INPUT_A 0x6A /* High input enable register I/O[7..0] (Bank A) */ +#define SX1509_REG_RESET 0x7D /* Software reset register */ +#define SX1509_REG_TEST_1 0x7E /* Test register 1 */ +#define SX1509_REG_TEST_2 0x7F /* Test register 2 */ + +/* Values being used for Soft reset of SX1509 */ +#define SX1509_SOFT_RESET_REG_VALUE_1 0x12 +#define SX1509_SOFT_RESET_REG_VALUE_2 0x34 + +static uint8_t SX1509_REG_T_ON[16] = { + SX1509_REG_T_ON_0, SX1509_REG_T_ON_1, SX1509_REG_T_ON_2, SX1509_REG_T_ON_3, + SX1509_REG_T_ON_4, SX1509_REG_T_ON_5, SX1509_REG_T_ON_6, SX1509_REG_T_ON_7, + SX1509_REG_T_ON_8, SX1509_REG_T_ON_9, SX1509_REG_T_ON_10, SX1509_REG_T_ON_11, + SX1509_REG_T_ON_12, SX1509_REG_T_ON_13, SX1509_REG_T_ON_14, SX1509_REG_T_ON_15 +}; + +static uint8_t SX1509_REG_OFF[16] = { + SX1509_REG_OFF_0, SX1509_REG_OFF_1, SX1509_REG_OFF_2, SX1509_REG_OFF_3, + SX1509_REG_OFF_4, SX1509_REG_OFF_5, SX1509_REG_OFF_6, SX1509_REG_OFF_7, + SX1509_REG_OFF_8, SX1509_REG_OFF_9, SX1509_REG_OFF_10, SX1509_REG_OFF_11, + SX1509_REG_OFF_12, SX1509_REG_OFF_13, SX1509_REG_OFF_14, SX1509_REG_OFF_15 +}; + +#if 0 +static uint8_t SX1509_REG_I_ON[16] = { + SX1509_REG_I_ON_0, SX1509_REG_I_ON_1, SX1509_REG_I_ON_2, SX1509_REG_I_ON_3, + SX1509_REG_I_ON_4, SX1509_REG_I_ON_5, SX1509_REG_I_ON_6, SX1509_REG_I_ON_7, + SX1509_REG_I_ON_8, SX1509_REG_I_ON_9, SX1509_REG_I_ON_10, SX1509_REG_I_ON_11, + SX1509_REG_I_ON_12, SX1509_REG_I_ON_13, SX1509_REG_I_ON_14, SX1509_REG_I_ON_15 }; +#endif + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_raw_read + ** + ** DESCRIPTION : Read the register value from IO Expander with LED + ** driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register address and pointer + ** to value read. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus ioexp_led_raw_read(const I2C_Dev *i2c_dev, + uint8_t regAddress, + uint8_t *regValue) +{ + ReturnStatus status = RETURN_NOTOK; + I2C_Handle sx1509_handle = i2c_get_handle(i2c_dev->bus); + uint16_t value = 0x0000; + if (!sx1509_handle) { + LOGGER_ERROR("SX1509:ERROR:: Failed to get I2C Bus for SX1509 0x%02x " + "on bus 0x%02x.\n", i2c_dev->slave_addr, i2c_dev->bus); + } else { + status = i2c_reg_read(sx1509_handle, i2c_dev->slave_addr, regAddress, + &value, 1); + if (status == RETURN_OK) { + *regValue = (uint8_t)(value); + } + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_raw_write + ** + ** DESCRIPTION : Write the register value(s) to IO Expander with LED + ** driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register address, value 1 + ** & value 2 to be written and No of bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus ioexp_led_raw_write(const I2C_Dev *i2c_dev, + uint8_t regAddress, + uint8_t regValue1, + uint8_t regValue2, + uint8_t noOfBytes) +{ + ReturnStatus status = RETURN_NOTOK; + I2C_Handle sx1509_handle = i2c_get_handle(i2c_dev->bus); + uint16_t value = 0x00; + if (noOfBytes == 2) { + value = (regValue2<<8) | (regValue1); + value = htobe16(value); + } else { + value = regValue1; + } + if (!sx1509_handle) { + LOGGER_ERROR("SX1509:ERROR:: Failed to get I2C Bus for SX1509 0x%02x " + "on bus 0x%02x.\n", i2c_dev->slave_addr, i2c_dev->bus); + } else { + status = i2c_reg_write(sx1509_handle, i2c_dev->slave_addr, regAddress, + value, noOfBytes); + } +return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_get_data + ** + ** DESCRIPTION : Read the Data Register Value from IO Expander with LED + ** driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type and pointer + ** to value read. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_get_data(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t *regValue) +{ + ReturnStatus status = RETURN_OK; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_DATA_A) : (SX1509_REG_DATA_B); + status = ioexp_led_raw_read(i2c_dev, regAddress, regValue); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_set_data + ** + ** DESCRIPTION : Write the Data Register Value(s) into IO Expander with + ** LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, value 1 & + ** value 2 to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_set_data(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t regValue1, + uint8_t regValue2) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_DATA_A) : (SX1509_REG_DATA_B); + if (regType == SX1509_REG_AB) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, regValue1, + regValue2, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_set_on_time + ** + ** DESCRIPTION : Write the ON Time Register Value into IO Expander + ** with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, index to On time Register + ** and value to be written. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_set_on_time(const I2C_Dev *i2c_dev, uint8_t index, + uint8_t tOnRegValue) +{ + ReturnStatus status = RETURN_OK; + status = ioexp_led_raw_write(i2c_dev, SX1509_REG_T_ON[index], + tOnRegValue, 0, 1); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_set_off_time + ** + ** DESCRIPTION : Write the OFF Time Register Value into IO Expander + ** with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, index to Off time Register + ** and value to be written. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_set_off_time(const I2C_Dev *i2c_dev, uint8_t index, + uint8_t tOffRegValue) +{ + ReturnStatus status = RETURN_OK; + status = ioexp_led_raw_write(i2c_dev, SX1509_REG_OFF[index], + tOffRegValue, 0, 1); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_software_reset + ** + ** DESCRIPTION : Do LED SX159 Soft Reset by writing 0x12 followed by + ** 0x34 on software reset register. + ** + ** ARGUMENTS : Subsystem and Slave address. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_software_reset(const I2C_Dev *i2c_dev) +{ + ReturnStatus status = RETURN_OK; + + status = ioexp_led_raw_write(i2c_dev, SX1509_REG_RESET, + SX1509_SOFT_RESET_REG_VALUE_1, 0, 1); + if (status == RETURN_OK) { + + status = ioexp_led_raw_write(i2c_dev, SX1509_REG_RESET, + SX1509_SOFT_RESET_REG_VALUE_2, 0, 1); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_inputbuffer + ** + ** DESCRIPTION : Write the Input Diable Input Buffer Register Value(s) + ** into IO Expander with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, value 1 & + ** value 2 to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_config_inputbuffer(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t inputBuffRegValue1, + uint8_t inputBuffRegValue2) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_INPUT_DISABLE_A) : (SX1509_REG_INPUT_DISABLE_B); + if (regType == SX1509_REG_AB) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, inputBuffRegValue1, + inputBuffRegValue2, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_pullup + ** + ** DESCRIPTION : Write the Pull_up Register Value(s) into IO Expander + ** with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, value 1 & + ** value 2 to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_config_pullup(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t pullUpRegValue1, + uint8_t pullUpRegValue2) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_PULL_UP_A) : (SX1509_REG_PULL_UP_B); + if (regType == SX1509_REG_AB) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, pullUpRegValue1, + pullUpRegValue2, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_pulldown + ** + ** DESCRIPTION : Write the Pull Down Register Value(s) into IO Expander + ** with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, value 1 & + ** value 2 to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_config_pulldown(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t pullDownRegValue1, + uint8_t pullDownRegValue2) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_PULL_DOWN_A) : (SX1509_REG_PULL_DOWN_B); + if (regType == SX1509_REG_AB) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, pullDownRegValue1, + pullDownRegValue2, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_opendrain + ** + ** DESCRIPTION : Write the Open drain Register Value(s) into IO Expander + ** with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, value 1 & + ** value 2 to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_config_opendrain(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t openDrainRegValue1, + uint8_t openDrainRegValue2) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_OPEN_DRAIN_A) : (SX1509_REG_OPEN_DRAIN_B); + if (regType == SX1509_REG_AB) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, openDrainRegValue1, + openDrainRegValue2, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_data_direction + ** + ** DESCRIPTION : Write the Direction Register Value(s) into IO Expander + ** with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, value 1 & + ** value 2 to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_config_data_direction(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t directionRegValue1, + uint8_t directionRegValue2) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_DIR_A) : (SX1509_REG_DIR_B); + if (regType == SX1509_REG_AB) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, directionRegValue1, + directionRegValue2, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_polarity + ** + ** DESCRIPTION : Write the Polarity Register Value(s) into IO Expander + ** with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, value 1 & + ** value 2 to be written. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_config_polarity(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t polarityRegValue1, + uint8_t polarityRegValue2) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_POLARITY_A) : (SX1509_REG_POLARITY_B); + if (regType == SX1509_REG_AB) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, polarityRegValue1, + polarityRegValue2, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_clock + ** + ** DESCRIPTION : Write the Clock management Register Value into IO + ** Expander with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address and value to be written. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +/* RegClock: + * 6:5 - Oscillator frequency souce + * 00: off, 01: external input, 10: internal 2MHz, 11: reserved + * 4 - OSCIO pin function + * 0: input, 1 ouptut + * 3:0 - Frequency of oscout pin + * 0: LOW, 0xF: high, else fOSCOUT = FoSC/(2^(RegClock[3:0]-1)) + */ +ReturnStatus ioexp_led_config_clock(const I2C_Dev *i2c_dev, uint8_t oscSource, + uint8_t oscPin) +{ + ReturnStatus status = RETURN_OK; + uint8_t regValue = 0; + + regValue = oscSource << 5; + regValue |= oscPin << 4; + + status = ioexp_led_raw_write(i2c_dev, SX1509_REG_CLOCK, regValue, 0, 1); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_misc + ** + ** DESCRIPTION : Write the Miscellaneous device settings Register Value + ** into IO Expander with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address and value to be written. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_config_misc(const I2C_Dev *i2c_dev, uint8_t regValue) +{ + ReturnStatus status = RETURN_OK; + status = ioexp_led_raw_write(i2c_dev, SX1509_REG_MISC, regValue, 0, 1); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_enable_leddriver + ** + ** DESCRIPTION : Write the LED driver enable Value(s) into IO Expander + ** with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, value 1 & + ** value 2 to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_enable_leddriver(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t ledEnableRegValue1, + uint8_t ledEnableRegValue2) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_LED_DRIVER_ENABLE_A) : + (SX1509_REG_LED_DRIVER_ENABLE_B); + if (regType == SX1509_REG_AB) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, ledEnableRegValue1, + ledEnableRegValue2, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_read_testregister_1 + ** + ** DESCRIPTION : Read the Test Register Value from IO Expander with LED + ** driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address and pointer to value read. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_read_testregister_1(const I2C_Dev *i2c_dev, + uint8_t *regValue) +{ + ReturnStatus status = RETURN_OK; + status = ioexp_led_raw_read(i2c_dev, SX1509_REG_TEST_1, regValue); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_interrupt + ** + ** DESCRIPTION : Write the Interrupt Mask Register Value(s) into IO + ** Expander with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, value 1 & + ** value 2 to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_config_interrupt(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t interruptMaskRegValue1, + uint8_t interruptMaskRegValue2) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_INTERRUPT_MASK_A) : + (SX1509_REG_INTERRUPT_MASK_B); + if (regType == SX1509_REG_AB) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, interruptMaskRegValue1, + interruptMaskRegValue2, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_edge_sense_A + ** + ** DESCRIPTION : Write the Edge Sense Register A(I/O[7:0]) Value(s) + ** into IO Expander with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, Low byte value + ** & High byte values of A to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +/* + * 00 : None + * 01 : Rising + * 10 : Falling + * 11 : Both + */ +ReturnStatus ioexp_led_config_edge_sense_A(const I2C_Dev *i2c_dev, + sx1509EdgeSenseRegType regType, + uint8_t edgeSenseLowARegValue, + uint8_t edgeSenseHighARegValue) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_EDGE_SENSE_REG_LOW) ? + (SX1509_REG_SENSE_LOW_A) : (SX1509_REG_SENSE_HIGH_A); + if (regType == SX1509_EDGE_SENSE_REG_LOW_HIGH) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, edgeSenseLowARegValue, + edgeSenseHighARegValue, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_edge_sense_B + ** + ** DESCRIPTION : Write the Edge Sense Register B(I/O[15:8]) Value(s) + ** into IO Expander with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, Low byte value + ** & High byte values of B to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_config_edge_sense_B(const I2C_Dev *i2c_dev, + sx1509EdgeSenseRegType regType, + uint8_t edgeSenseLowBRegValue, + uint8_t edgeSenseHighBRegValue) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_EDGE_SENSE_REG_LOW) ? + (SX1509_REG_SENSE_LOW_B) : (SX1509_REG_SENSE_HIGH_B); + if (regType == SX1509_EDGE_SENSE_REG_LOW_HIGH) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, edgeSenseLowBRegValue, + edgeSenseHighBRegValue, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_debounce + ** + ** DESCRIPTION : Write the Debounce time Value into IO Expander with + ** LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address and debounce Time(ms) to be + ** written. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +/* Debounce time-to-byte map: (assuming fOsc = 2MHz; 2^(n-1)) + * 0: 0.5ms 1: 1ms + * 2: 2ms 3: 4ms + * 4: 8ms 5: 16ms + * 6: 32ms 7: 64ms + */ +ReturnStatus ioexp_led_config_debounce_time(const I2C_Dev *i2c_dev, + uint8_t debounceTime) +{ + ReturnStatus status = RETURN_OK; + uint8_t index = 0; + uint8_t regValue = 0; + + for (index = 0; index < 8; index++) { + if (debounceTime & (1 << index)) { + regValue = index + 1; + break; + } + } + + status = ioexp_led_raw_write(i2c_dev, SX1509_REG_DEBOUNCE_CONFIG, + regValue, 0, 1); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_enable_debounce + ** + ** DESCRIPTION : Write the Debounce enable Register Value into + ** IO Expander with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, value 1 & + ** value 2 to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_enable_debounce(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t debounceEnableRegValue1, + uint8_t debounceEnableRegValue2) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_DEBOUNCE_ENABLE_A) : (SX1509_REG_DEBOUNCE_ENABLE_B); + if (regType == SX1509_REG_AB) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, debounceEnableRegValue1, + debounceEnableRegValue2, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_read_interrupt_source + ** + ** DESCRIPTION : Read the Interrupt Source Register Value from IO + ** Expander with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type and pointer + ** to value read. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_get_interrupt_source(const I2C_Dev *i2c_dev, + uint16_t *intPins) +{ + ReturnStatus status = RETURN_OK; + uint8_t regValueA = 0; + uint8_t regValueB = 0; + + status = ioexp_led_raw_read(i2c_dev, SX1509_REG_INTERRUPT_SOURCE_A, + ®ValueA); + if (status != RETURN_OK) { + return status; + } + status = ioexp_led_raw_read(i2c_dev, SX1509_REG_INTERRUPT_SOURCE_B, + ®ValueB); + *intPins = (uint16_t) ((regValueB << 8) | regValueA); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_clear_interrupt_source + ** + ** DESCRIPTION : Clear the Interrupt status. + ** + ** ARGUMENTS : Subsystem and Slave address. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_clear_interrupt_source(const I2C_Dev *i2c_dev) +{ + ReturnStatus status = RETURN_OK; + status = ioexp_led_raw_write(i2c_dev, SX1509_REG_INTERRUPT_SOURCE_B, + 0xFF, 0xFF, 2); + return status; +} diff --git a/firmware/psu/src/drivers/GpioNative.c b/firmware/psu/src/drivers/GpioNative.c new file mode 100644 index 0000000000..81ca161b50 --- /dev/null +++ b/firmware/psu/src/drivers/GpioNative.c @@ -0,0 +1,273 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#include "GpioNative.h" + +#include "common/inc/global/OC_CONNECT1.h" +#include "inc/common/global_header.h" + +#include +#include + +#include + +static GateMutex_Handle s_cb_data_mutex; + +/***************************************************************************** + ** FUNCTION NAME : GpioNative_probe + ** + ** DESCRIPTION : probe function called as part of POST + ** + ** ARGUMENTS : none + ** + ** RETURN TYPE : OCGPIO_SUCCESS or OCGPIO_FAILURE + ** + *****************************************************************************/ +static int GpioNative_probe(void) { + //This probe function is just a dummy as we are all ready accessing EC. + return OCGPIO_SUCCESS; +} +/***************************************************************************** + ** FUNCTION NAME : GpioNative_init + ** + ** DESCRIPTION : inits as part of system bootup + ** + ** ARGUMENTS : NONE + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void GpioNative_init(void) { + s_cb_data_mutex = GateMutex_create(NULL, NULL); +} + +/***************************************************************************** + ** FUNCTION NAME : GpioNative_write + ** + ** DESCRIPTION : gpio write for the required pin and value + ** + ** ARGUMENTS : pin index , value + ** + ** RETURN TYPE : OCGPIO_SUCCESS or OCGPIO_FAILURE + ** + *****************************************************************************/ +static int GpioNative_write(const OcGpio_Pin *pin, bool value) { + if (pin->hw_cfg & OCGPIO_CFG_INVERT) { + value = !value; + } + GPIO_write(pin->idx, value); + return OCGPIO_SUCCESS; +} + +/***************************************************************************** + ** FUNCTION NAME : GpioNative_read + ** + ** DESCRIPTION : does a gpio read for the provided pin + ** + ** ARGUMENTS : pin index + ** + ** RETURN TYPE : int + ** + *****************************************************************************/ +static int GpioNative_read(const OcGpio_Pin *pin) { + bool value = GPIO_read(pin->idx); + if (pin->hw_cfg & OCGPIO_CFG_INVERT) { + value = !value; + } + return value; +} + +/***************************************************************************** + ** FUNCTION NAME : GetBank + ** + ** DESCRIPTION : configure the pins with the provided config + ** + ** ARGUMENTS : pointer to pin, config + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioNative_configure(const OcGpio_Pin *pin, uint32_t cfg) { + /* TODO: translate config values to account for inversion + * eg. If inverted, change rising edge trigger to falling edge + */ + /* TODO: need to be careful in multi-subscriber case - if we reconfigure, + * the TI driver clears any interrupts on this pin, so we could potentially + * miss edges for already subscribed pins + */ + const OcGpio_HwCfg hw_cfg = { .uint16 = pin->hw_cfg }; + const OcGpio_ioCfg io_cfg = { .uint32 = cfg }; + uint32_t ti_cfg = 0x00; + + if (cfg & OCGPIO_CFG_INPUT) { + ti_cfg |= GPIO_CFG_INPUT; + ti_cfg |= (hw_cfg.in_cfg << GPIO_CFG_IO_LSB); /* Include PU/PD cfg */ + + /* Process interrupt config (respect inversion settings) */ + if (hw_cfg.invert) { + switch ((uint32_t)io_cfg.int_cfg << GPIO_CFG_INT_LSB) { + case GPIO_CFG_IN_INT_FALLING: + ti_cfg |= GPIO_CFG_IN_INT_RISING; + break; + case GPIO_CFG_IN_INT_RISING: + ti_cfg |= GPIO_CFG_IN_INT_FALLING; + break; + case GPIO_CFG_IN_INT_LOW: + ti_cfg |= GPIO_CFG_IN_INT_HIGH; + break; + case GPIO_CFG_IN_INT_HIGH: + ti_cfg |= GPIO_CFG_IN_INT_LOW; + break; + + case OCGPIO_CFG_INT_NONE: + case GPIO_CFG_IN_INT_BOTH_EDGES: + default: + /* No change */ + ti_cfg |= (io_cfg.int_cfg << GPIO_CFG_INT_LSB); + break; + } + } else { + ti_cfg |= (io_cfg.int_cfg << GPIO_CFG_INT_LSB); + } + } else { + ti_cfg |= GPIO_CFG_OUTPUT; + ti_cfg |= (hw_cfg.out_cfg << GPIO_CFG_IO_LSB); /* Include od/pu/pd cfg */ + ti_cfg |= (hw_cfg.out_str << GPIO_CFG_OUT_STRENGTH_LSB); + ti_cfg |= (io_cfg.default_val << GPIO_CFG_OUT_BIT); + } + GPIO_setConfig(pin->idx, ti_cfg); + return OCGPIO_SUCCESS; +} + +/* TODO: since every GPIO driver will probably need this, might be worth + * moving out to GPIO I/F module + */ +typedef struct GpioCallbackData { + const OcGpio_Pin *pin; + OcGpio_CallbackFn callback; + void *context; + struct GpioCallbackData *next; /*!< Pointer to next pin subscriber */ +} GpioCallbackData; + +/* This allows us to work around the native GPIO driver's poor design and allow + * a context pointer to be passed to the calling function + */ +static GpioCallbackData *cb_data[OC_EC_GPIOCOUNT]; + +/* + */ +/***************************************************************************** + ** FUNCTION NAME : _nativeCallback + ** + ** DESCRIPTION : Wrapper to allow us to map TI-GPIO callback to all + ** our subscribers (with context passing) + ** + ** ARGUMENTS : pin index + ** + ** RETURN TYPE : NONE + ** + *****************************************************************************/ +static void _nativeCallback(unsigned int idx) { + GpioCallbackData *cbData = cb_data[idx]; + while (cbData) { + cbData->callback(cbData->pin, cbData->context); + cbData = cbData->next; + } + return; +} + +/***************************************************************************** + ** FUNCTION NAME : GpioNative_setCallback + ** + ** DESCRIPTION : set call back function for handling interrupts. + ** + ** ARGUMENTS : pointer to pin , call back function pointer , context + ** to be passed as argument to the call back function + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioNative_setCallback(const OcGpio_Pin *pin, + OcGpio_CallbackFn callback, + void *context) { + /* TODO: we may want to support callback removal at some point */ + if (!callback) { + return OCGPIO_FAILURE; + } + + GpioCallbackData *cb_entry = malloc(sizeof(GpioCallbackData)); + if (!cb_entry) { + LOGGER_ERROR("Unable to malloc GPIO callback"); + return OCGPIO_FAILURE; + } + + *cb_entry = (GpioCallbackData){ + .pin = pin, + .callback = callback, + .context = context, + }; + + /* find next blank entry & assign new callback data (protect against + * multiple tasks accessing simultaneously) + * Note: we don't need to worry about the actual callback function using + * a mutex, since the 'next' pointer assignment is atomic */ + const IArg mutexKey = GateMutex_enter(s_cb_data_mutex); + { + GpioCallbackData **next = &cb_data[pin->idx]; + while (*next) { + next = &(*next)->next; + } + *next = cb_entry; + } + GateMutex_leave(s_cb_data_mutex, mutexKey); + GPIO_setCallback(pin->idx, _nativeCallback); + return OCGPIO_SUCCESS; +} + +/* TODO: what if multiple tasks are sharing a pin and one of them + * disables the interrupt? */ +/***************************************************************************** + ** FUNCTION NAME : GpioNative_disableInt + ** + ** DESCRIPTION : identify the bank. + ** + ** ARGUMENTS : pin index + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioNative_disableInt(const OcGpio_Pin *pin) { + GPIO_disableInt(pin->idx); + return OCGPIO_SUCCESS; +} + +/***************************************************************************** + ** FUNCTION NAME : GpioNative_enableInt + ** + ** DESCRIPTION : enable interrupt for a particular pin + ** + ** ARGUMENTS : pin index + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioNative_enableInt(const OcGpio_Pin *pin) { + GPIO_enableInt(pin->idx); + return OCGPIO_SUCCESS; +} + +const OcGpio_FnTable GpioNative_fnTable = { + .probe = GpioNative_probe, + .write = GpioNative_write, + .read = GpioNative_read, + .configure = GpioNative_configure, + .setCallback = GpioNative_setCallback, + .disableInt = GpioNative_disableInt, + .enableInt = GpioNative_enableInt, +}; diff --git a/firmware/psu/src/drivers/GpioNative.h b/firmware/psu/src/drivers/GpioNative.h new file mode 100644 index 0000000000..68ec2711e2 --- /dev/null +++ b/firmware/psu/src/drivers/GpioNative.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#ifndef _GPIONATIVE_H_ +#define _GPIONATIVE_H_ + +#include "OcGpio.h" + +extern const OcGpio_FnTable GpioNative_fnTable; + +/* + * Must be called before using the GPIO Native driver + */ +void GpioNative_init(void); + +#endif /* _GPIONATIVE_H_ */ diff --git a/firmware/psu/src/drivers/GpioSX1509.c b/firmware/psu/src/drivers/GpioSX1509.c new file mode 100644 index 0000000000..9f1ffa5fed --- /dev/null +++ b/firmware/psu/src/drivers/GpioSX1509.c @@ -0,0 +1,503 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#include "devices/i2c/threaded_int.h" +#include "GpioSX1509.h" +#include "helpers/memory.h" +#include "inc/common/global_header.h" +#include "inc/devices/sx1509.h" + +#include +#include + +/***************************************************************************** + ** FUNCTION NAME : GetBank + ** + ** DESCRIPTION : identify the bank. + ** + ** ARGUMENTS : pin index + ** + ** RETURN TYPE : SX1509_REG_B or SX1509_REG_A + ** + *****************************************************************************/ +static sx1509RegType GetBank(uint8_t pin_idx) { + return (pin_idx > 7) ? SX1509_REG_B : SX1509_REG_A; +} + +/***************************************************************************** + ** FUNCTION NAME : RelativePinIdx + ** + ** DESCRIPTION : identify the relative pin index. + ** + ** ARGUMENTS : pin idx + ** + ** RETURN TYPE : uint8_t + ** + *****************************************************************************/ +static uint8_t RelativePinIdx(uint8_t pin_idx) { + return (pin_idx > 7) ? (pin_idx - 8) : pin_idx; +} + +/***************************************************************************** + ** FUNCTION NAME : HandleIRQ + ** + ** DESCRIPTION : IRQ handler - reads in triggered interrupts and + ** dispatches + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : none + ** + *****************************************************************************/ +static void HandleIRQ(void *context) { + const OcGpio_Port *port = context; + const SX1509_Cfg *sx_cfg = port->cfg; + SX1509_Obj *obj = port->object_data; + + /* Figure out which pin this interrupt came from */ + /* TODO: this seems risky - what if an interrupt occurs between reading + * src and clearing it? It's dumb that the IC doesn't clear the irq on + * reading the source */ + uint16_t irq_src; + ioexp_led_get_interrupt_source(&sx_cfg->i2c_dev, &irq_src); + ioexp_led_clear_interrupt_source(&sx_cfg->i2c_dev); + + // Dispatch the interrupt handlers + int pin_idx = 0; + while (irq_src) { + if (irq_src & 0x01) { + SX1509CallbackData *cbData = obj->cb_data[pin_idx]; + while (cbData) { + cbData->callback(cbData->pin, cbData->context); + cbData = cbData->next; + } + } + ++pin_idx; + irq_src >>= 1; + } +} + +/***************************************************************************** + ** FUNCTION NAME : GpioSX1509_probe + ** + ** DESCRIPTION : post related api which check connected to the device. + ** + ** ARGUMENTS : pointer to port + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioSX1509_probe(const OcGpio_Port *port) { + /* if we are able to read configuration register this means PCA device is accessible*/ + const SX1509_Cfg *sx_cfg = port->cfg; + SX1509_Obj *obj = port->object_data; + uint8_t input_reg; + if (ioexp_led_get_data(&sx_cfg->i2c_dev, 0, &input_reg) != RETURN_OK) { + return OCGPIO_FAILURE; + } + return OCGPIO_SUCCESS; +} + +/***************************************************************************** + ** FUNCTION NAME : GpioSX1509_init + ** + ** DESCRIPTION : init . + ** + ** ARGUMENTS : pointer to the port + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioSX1509_init(const OcGpio_Port *port) { + const SX1509_Cfg *sx_cfg = port->cfg; + SX1509_Obj *obj = port->object_data; + + obj->mutex = GateMutex_create(NULL, NULL); + for (int i = 0; i < SX1509_NUM_BANKS; ++i) { + obj->regs[i] = (SX1509_Registers){ + /* We only need to set the non-zero registers */ + .direction = 0xff, + .data = 0xff, + .int_mask = 0xff, + }; + } + + memset(obj->cb_data, 0, sizeof(obj->cb_data)); + + /* Make sure the IC is set to default config */ + if (ioexp_led_software_reset(&sx_cfg->i2c_dev) + != RETURN_OK) { + return OCGPIO_FAILURE; + } + + /* Register the SX1509's own IRQ pin (optional) */ + if (sx_cfg->pin_irq) { + const uint32_t pin_irq_cfg = OCGPIO_CFG_INPUT | OCGPIO_CFG_INT_FALLING; + if (OcGpio_configure(sx_cfg->pin_irq, pin_irq_cfg) < OCGPIO_SUCCESS) { + return OCGPIO_FAILURE; + } + + /* Use a threaded interrupt to handle IRQ */ + // ThreadedInt_Init(sx_cfg->pin_irq, HandleIRQ, (void *)port); + } + return OCGPIO_SUCCESS; +} + +/***************************************************************************** + ** FUNCTION NAME : GpioSX1509_write + ** + ** DESCRIPTION : write into a particular pin + ** + ** ARGUMENTS : pointer to pin, value + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioSX1509_write(const OcGpio_Pin *pin, bool value) { + const SX1509_Cfg *sx_cfg = pin->port->cfg; + SX1509_Obj *obj = pin->port->object_data; + int res = OCGPIO_FAILURE; + + const IArg mutexKey = GateMutex_enter(obj->mutex); + { + /* Reading the value of output pins seems unreliable on the SX1509, + * so we cache the output value instead (it's faster anyway) + */ + const sx1509RegType bank = GetBank(pin->idx); + const uint8_t pin_idx = RelativePinIdx(pin->idx); + const uint8_t new_reg_value = set_bit8(obj->regs[bank].data, + pin_idx, value); + if (ioexp_led_set_data(&sx_cfg->i2c_dev, bank, new_reg_value, 0x00) + != RETURN_OK) { + goto cleanup; + } + obj->regs[bank].data = new_reg_value; + res = OCGPIO_SUCCESS; + } +cleanup: + GateMutex_leave(obj->mutex, mutexKey); + return res; +} + +/***************************************************************************** + ** FUNCTION NAME : GpioSX1509_read + ** + ** DESCRIPTION : read a particular pin + ** + ** ARGUMENTS : pointer to pin + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioSX1509_read(const OcGpio_Pin *pin) { + const SX1509_Cfg *sx_cfg = pin->port->cfg; + + /* We don't need a mutex here since i2c driver protects against + * simultaneous access and we're just reading a value */ + const sx1509RegType bank = GetBank(pin->idx); + const uint8_t pin_idx = RelativePinIdx(pin->idx); + uint8_t input_reg; + if (ioexp_led_get_data(&sx_cfg->i2c_dev, bank, &input_reg) != RETURN_OK) { + return OCGPIO_FAILURE; + } + + return (input_reg >> pin_idx) & 0x01; +} + +/* TODO: this mapping is pretty gross with the shifts */ +static const uint8_t EDGE_SENSE_MAP[] = { + [OCGPIO_CFG_INT_NONE >> OCGPIO_CFG_INT_LSB] = 0x00, /* 00 */ + [OCGPIO_CFG_INT_RISING >> OCGPIO_CFG_INT_LSB] = 0x01, /* 01 */ + [OCGPIO_CFG_INT_FALLING >> OCGPIO_CFG_INT_LSB] = 0x02, /* 10 */ + [OCGPIO_CFG_INT_BOTH_EDGES >> OCGPIO_CFG_INT_LSB] = 0x03, /* 11 */ +}; + +/* TODO: handle things nicely if we get a failure part way through config - + * right now we break the rule of the other functions to only store the new + * value if the IC accepted the changed value */ +/***************************************************************************** + ** FUNCTION NAME : GpioSX1509_configure + ** + ** DESCRIPTION : configure the pins with the provided config + ** + ** ARGUMENTS : pointer to pin, sx1509 config + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioSX1509_configure(const OcGpio_Pin *pin, uint32_t cfg) { + const SX1509_Cfg *sx_cfg = pin->port->cfg; + SX1509_Obj *obj = pin->port->object_data; + int res = OCGPIO_FAILURE; + + const OcGpio_ioCfg io_cfg = { .uint32 = cfg }; + + const sx1509RegType bank = GetBank(pin->idx); + const uint8_t pin_idx = RelativePinIdx(pin->idx); + SX1509_Registers *reg = &obj->regs[bank]; + + const IArg mutexKey = GateMutex_enter(obj->mutex); + { + /* Invert the polarity (1) if necessary */ + reg->polarity = set_bit8(reg->polarity, pin_idx, + pin->hw_cfg & OCGPIO_CFG_INVERT); + if (ioexp_led_config_polarity(&sx_cfg->i2c_dev, bank, reg->polarity, + 0x00) != RETURN_OK) { + goto cleanup; + } + + bool pu_en; + bool pd_en; + + /* Set pull-up/down registers */ + /* Set output-specific registers if applicable */ + if (io_cfg.dir == OCGPIO_CFG_OUTPUT) { + /* Enable (1) open drain */ + const bool od_en = (pin->hw_cfg & OCGPIO_CFG_OUT_OD_MASK); + reg->open_drain = set_bit8(reg->open_drain, pin_idx, od_en); + if (ioexp_led_config_opendrain(&sx_cfg->i2c_dev, + bank, + reg->open_drain, + 0x00) != RETURN_OK) { + goto cleanup; + } + + /* Disable (1) the input buffer */ + reg->input_buf_disable = set_bit8(reg->input_buf_disable, pin_idx, + 1); + if (ioexp_led_config_inputbuffer(&sx_cfg->i2c_dev, + bank, + reg->input_buf_disable, + 0x00) != RETURN_OK) { + goto cleanup; + } + + /* Set default value */ + GpioSX1509_write(pin, io_cfg.default_val); + + /* TODO: this is kind of gross, not sure if it's worth keeping + * compatibility with TI-GPIO cfg */ + pu_en = ((pin->hw_cfg & OCGPIO_CFG_OUT_OD_MASK) == + OCGPIO_CFG_OUT_OD_PU); + pd_en = ((pin->hw_cfg & OCGPIO_CFG_OUT_OD_MASK) == + OCGPIO_CFG_OUT_OD_PD); + } else { + /* Enable (0) the input buffer */ + reg->input_buf_disable = set_bit8(reg->input_buf_disable, pin_idx, + 0); + if (ioexp_led_config_inputbuffer(&sx_cfg->i2c_dev, + bank, + reg->input_buf_disable, + 0x00) != RETURN_OK) { + goto cleanup; + } + + /* Set interrupt edge detection */ + /* This is a bit tricky since we need to set a pair of bits */ + const uint16_t EDGE_SENSE_MASK = 0x03 << (pin_idx * 2); + reg->edge_sense &= ~EDGE_SENSE_MASK; + reg->edge_sense |= EDGE_SENSE_MAP[io_cfg.int_cfg] << (pin_idx * 2); + + switch (bank) { + case SX1509_REG_A: + if (ioexp_led_config_edge_sense_A( + &sx_cfg->i2c_dev, + SX1509_EDGE_SENSE_REG_LOW_HIGH, + LOBYTE(reg->edge_sense), + HIBYTE(reg->edge_sense)) != RETURN_OK) { + goto cleanup; + } + break; + case SX1509_REG_B: + if (ioexp_led_config_edge_sense_B( + &sx_cfg->i2c_dev, + SX1509_EDGE_SENSE_REG_LOW_HIGH, + LOBYTE(reg->edge_sense), + HIBYTE(reg->edge_sense)) != RETURN_OK) { + goto cleanup; + } + break; + default: + LOGGER_ERROR("SX1509: Unknown bank number: %d\n", bank); + goto cleanup; + } + + pu_en = ((pin->hw_cfg & OCGPIO_CFG_IN_PULL_MASK) == + OCGPIO_CFG_IN_PU); + pd_en = ((pin->hw_cfg & OCGPIO_CFG_IN_PULL_MASK) == + OCGPIO_CFG_IN_PD); + } + + /* Set pull-up/down registers */ + reg->pull_up = set_bit8(reg->pull_up, pin_idx, pu_en); + if (ioexp_led_config_pullup(&sx_cfg->i2c_dev, + bank, + reg->pull_up, + 0x00) != RETURN_OK) { + goto cleanup; + } + + reg->pull_down = set_bit8(reg->pull_down, pin_idx, pd_en); + if (ioexp_led_config_pulldown(&sx_cfg->i2c_dev, + bank, + reg->pull_down, + 0x00) != RETURN_OK) { + goto cleanup; + } + + /* Set pin direction (0 output, 1 input) */ + reg->direction = set_bit8(reg->direction, pin_idx, io_cfg.dir); + if (ioexp_led_config_data_direction(&sx_cfg->i2c_dev, + bank, + reg->direction, + 0x00) != RETURN_OK) { + goto cleanup; + } + + /* The SX1509 doesn't support drive strength */ + res = OCGPIO_SUCCESS; + } +cleanup: + GateMutex_leave(obj->mutex, mutexKey); + return res; +} + +/***************************************************************************** + ** FUNCTION NAME : GpioSX1509_setCallback + ** + ** DESCRIPTION : set call back function for handling interrupts. + ** + ** ARGUMENTS : pointer to pin , call back function pointer , context + ** to be passed as argument to the call back function + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioSX1509_setCallback(const OcGpio_Pin *pin, + OcGpio_CallbackFn callback, + void *context) { + SX1509_Obj *obj = pin->port->object_data; + + /* TODO: we may want to support callback removal at some point */ + if (!callback) { + return OCGPIO_FAILURE; + } + + SX1509CallbackData *cb_entry = malloc(sizeof(SX1509CallbackData)); + if (!cb_entry) { + LOGGER_ERROR("Unable to malloc GPIO callback"); + return OCGPIO_FAILURE; + } + + *cb_entry = (SX1509CallbackData){ + .pin = pin, + .callback = callback, + .context = context, + }; + + /* find next blank entry & assign new callback data (protect against + * multiple tasks accessing simultaneously) + * Note: we don't need to worry about the actual callback function using + * a mutex, since the 'next' pointer assignment is atomic */ + const IArg mutexKey = GateMutex_enter(obj->mutex); + { + SX1509CallbackData **next = &obj->cb_data[pin->idx]; + while (*next) { + next = &(*next)->next; + } + *next = cb_entry; + } + GateMutex_leave(obj->mutex, mutexKey); + return OCGPIO_SUCCESS; +} + +/***************************************************************************** + ** FUNCTION NAME : GpioSX1509_disableInt + ** + ** DESCRIPTION : disable interrupts for the provided pin. + ** + ** ARGUMENTS : pointer to the pin + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioSX1509_disableInt(const OcGpio_Pin *pin) { + const SX1509_Cfg *sx_cfg = pin->port->cfg; + SX1509_Obj *obj = pin->port->object_data; + int res = OCGPIO_FAILURE; + + const sx1509RegType bank = GetBank(pin->idx); + const uint8_t pin_idx = RelativePinIdx(pin->idx); + SX1509_Registers *reg = &obj->regs[bank]; + + /* Disable (1) interrupt */ + const IArg mutexKey = GateMutex_enter(obj->mutex); + { + const uint8_t new_reg_value = set_bit8(reg->int_mask, pin_idx, 1); + if (ioexp_led_config_interrupt(&sx_cfg->i2c_dev, + bank, + new_reg_value, + 0x00) != RETURN_OK) { + goto cleanup; + } + reg->int_mask = new_reg_value; + res = OCGPIO_SUCCESS; + } +cleanup: + GateMutex_leave(obj->mutex, mutexKey); + return res; +} + +/***************************************************************************** + ** FUNCTION NAME : GpioSX1509_enableInt + ** + ** DESCRIPTION : enable interrupt on a gpio pin + ** + ** ARGUMENTS : pointer to the pin + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioSX1509_enableInt(const OcGpio_Pin *pin) { + const SX1509_Cfg *sx_cfg = pin->port->cfg; + SX1509_Obj *obj = pin->port->object_data; + int res = OCGPIO_FAILURE; + + const sx1509RegType bank = GetBank(pin->idx); + const uint8_t pin_idx = RelativePinIdx(pin->idx); + SX1509_Registers *reg = &obj->regs[bank]; + + /* Enable (0) interrupt */ + const IArg mutexKey = GateMutex_enter(obj->mutex); + { + const uint8_t new_reg_value = set_bit8(reg->int_mask, pin_idx, 0); + if (ioexp_led_config_interrupt(&sx_cfg->i2c_dev, + bank, + new_reg_value, + 0x00) != RETURN_OK) { + goto cleanup; + } + reg->int_mask = new_reg_value; + res = OCGPIO_SUCCESS; + } +cleanup: + GateMutex_leave(obj->mutex, mutexKey); + return res; +} + +const OcGpio_FnTable GpioSX1509_fnTable = { + .probe = GpioSX1509_probe, + .init = GpioSX1509_init, + .write = GpioSX1509_write, + .read = GpioSX1509_read, + .configure = GpioSX1509_configure, + .setCallback = GpioSX1509_setCallback, + .disableInt = GpioSX1509_disableInt, + .enableInt = GpioSX1509_enableInt, +}; diff --git a/firmware/psu/src/drivers/GpioSX1509.h b/firmware/psu/src/drivers/GpioSX1509.h new file mode 100644 index 0000000000..054a476f6e --- /dev/null +++ b/firmware/psu/src/drivers/GpioSX1509.h @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef _GPIOSX1509_H_ +#define _GPIOSX1509_H_ + +#include "common/inc/global/ocmp_frame.h" +#include "inc/devices/sx1509.h" +#include "OcGpio.h" + +#include + +#include + +#define SX1509_NUM_BANKS 2 +#define SX1509_PINS_PER_BANK 8 +#define SX1509_PIN_COUNT (SX1509_NUM_BANKS * SX1509_PINS_PER_BANK) + +extern const OcGpio_FnTable GpioSX1509_fnTable; + +typedef struct SX1509_Cfg { + I2C_Dev i2c_dev; + OcGpio_Pin *pin_irq; +} SX1509_Cfg; + +/* Private SX1509 driver data */ +typedef struct SX1509_Registers { + uint8_t data; + uint8_t input_buf_disable; + uint8_t pull_up; + uint8_t pull_down; + uint8_t open_drain; + uint8_t polarity; + uint8_t direction; + uint8_t int_mask; + uint16_t edge_sense; /*!< Could be split into high and low if needed */ +} SX1509_Registers; + +/* TODO: possibly dedupe with GpioNative */ +typedef struct SX1509CallbackData { + const OcGpio_Pin *pin; + OcGpio_CallbackFn callback; + void *context; + struct SX1509CallbackData *next; /*!< Pointer to next pin subscriber */ +} SX1509CallbackData; + +typedef struct SX1509_Obj { + GateMutex_Handle mutex; /*!< Prevent simultaneous editing of registers */ + SX1509_Registers regs[SX1509_NUM_BANKS]; + + SX1509CallbackData *cb_data[SX1509_PIN_COUNT]; +} SX1509_Obj; + +#endif /* _GPIOSX1509_H_ */ diff --git a/firmware/psu/src/drivers/OcGpio.c b/firmware/psu/src/drivers/OcGpio.c new file mode 100644 index 0000000000..579f53cd9b --- /dev/null +++ b/firmware/psu/src/drivers/OcGpio.c @@ -0,0 +1,10 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "drivers/OcGpio.h" + diff --git a/firmware/psu/src/drivers/OcGpio.h b/firmware/psu/src/drivers/OcGpio.h new file mode 100644 index 0000000000..1f881ff7a2 --- /dev/null +++ b/firmware/psu/src/drivers/OcGpio.h @@ -0,0 +1,265 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef _OCGPIO_H_ +#define _OCGPIO_H_ + +#include +#include + +/* OC-GPIO functions will return a negative on failure */ +#define OCGPIO_SUCCESS 0 +#define OCGPIO_FAILURE -1 + +typedef struct OcGpio_Pin OcGpio_Pin; +typedef struct OcGpio_Port OcGpio_Port; + +typedef void (*OcGpio_CallbackFn) (const OcGpio_Pin *pin, void *context); + +/* Interface virtual function definitions */ +typedef int (*OcGpio_initFn) (const OcGpio_Port *port); +typedef int (*OcGpio_writeFn) (const OcGpio_Pin *pin, bool value); +typedef int (*OcGpio_readFn) (const OcGpio_Pin *pin); +typedef int (*OcGpio_configFn) (const OcGpio_Pin *pin, uint32_t cfg); +typedef int (*OcGpio_setCallbackFn) (const OcGpio_Pin *pin, + OcGpio_CallbackFn callback, + void *context); +typedef int (*OcGpio_disableIntFn) (const OcGpio_Pin *pin); +typedef int (*OcGpio_enableIntFn) (const OcGpio_Pin *pin); + +typedef struct OcGpio_FnTable { + OcGpio_initFn probe; + OcGpio_initFn init; /*!< Port initialization - called once */ + OcGpio_writeFn write; + OcGpio_readFn read; + OcGpio_configFn configure; + OcGpio_setCallbackFn setCallback; + OcGpio_disableIntFn disableInt; + OcGpio_enableIntFn enableInt; +} OcGpio_FnTable; + +/*! A port defines a specific driver instance to route through */ +struct OcGpio_Port { + const OcGpio_FnTable *fn_table; /*!< virtual table for driver */ + const void *cfg; /*!< driver-specific config settings */ + void *object_data; /*!< driver-specific data (in RAM) */ +}; + +/*! A pin provides us with everything we need to route data to the appropriate + * driver - a port instance, a pin index, and board-sprcific configuration + * settings + */ +struct OcGpio_Pin { + const OcGpio_Port *port; /*!< Pointer to IO driver instance */ + uint16_t idx; /*!< Driver-specific index */ + uint16_t hw_cfg; /*!< Any special attributes for the pin (eg. invert) */ +}; + +/* + * OC GPIO hardware configuration settings - these are to be used in the board + * config file since they're largely depicted by the layout. One may provide + * separate input and output configurations for bi-directional pins + * + * Note: these definitions are compatible with the TI GPIO driver configs with + * a small amount of shifting + * ============================================================================ + */ +typedef union OcGpio_HwCfg { + struct { + bool invert:1; + uint16_t in_cfg:3; + uint16_t out_cfg:3; + uint16_t out_str:2; + }; + uint16_t uint16; +} OcGpio_HwCfg; + +#define OCGPIO_CFG_POL_MASK (((uint32_t) 1) << OCGPIO_CFG_POL_LSB) +#define OCGPIO_CFG_IN_PULL_MASK (((uint32_t) 6) << OCGPIO_CFG_IN_LSB) +#define OCGPIO_CFG_OUT_OD_MASK (((uint32_t) 7) << OCGPIO_CFG_OUT_LSB) +#define OCGPIO_CFG_OUT_STR_MASK (((uint32_t) 3) << OCGPIO_CFG_OUT_STR_LSB) + +#define OCGPIO_CFG_POL_LSB 0 +#define OCGPIO_CFG_IN_LSB 1 +#define OCGPIO_CFG_OUT_LSB 4 +#define OCGPIO_CFG_OUT_STR_LSB 7 + +#define OCGPIO_CFG_POL_STD (((uint32_t) 0) << OCGPIO_CFG_POL_LSB) /*!< Standard polarity */ +#define OCGPIO_CFG_INVERT (((uint32_t) 1) << OCGPIO_CFG_POL_LSB) /*!< Polarity inverted */ + +#define OCGPIO_CFG_IN_NOPULL (((uint32_t) 0) << OCGPIO_CFG_IN_LSB) /*!< Input pin has no PU/PD */ +#define OCGPIO_CFG_IN_PU (((uint32_t) 2) << OCGPIO_CFG_IN_LSB) /*!< Input pin has Pullup */ +#define OCGPIO_CFG_IN_PD (((uint32_t) 4) << OCGPIO_CFG_IN_LSB) /*!< Input pin has Pulldown */ + +#define OCGPIO_CFG_OUT_STD (((uint32_t) 0) << OCGPIO_CFG_OUT_LSB) /*!< Output pin is not Open Drain */ +#define OCGPIO_CFG_OUT_OD_NOPULL (((uint32_t) 2) << OCGPIO_CFG_OUT_LSB) /*!< Output pin is Open Drain */ +#define OCGPIO_CFG_OUT_OD_PU (((uint32_t) 4) << OCGPIO_CFG_OUT_LSB) /*!< Output pin is Open Drain w/ pull up */ +#define OCGPIO_CFG_OUT_OD_PD (((uint32_t) 6) << OCGPIO_CFG_OUT_LSB) /*!< Output pin is Open Drain w/ pull dn */ + +#define OCGPIO_CFG_OUT_STR_LOW (((uint32_t) 0) << OCGPIO_CFG_OUT_STR_LSB) /*!< Low drive strength */ +#define OCGPIO_CFG_OUT_STR_MED (((uint32_t) 1) << OCGPIO_CFG_OUT_STR_LSB) /*!< Medium drive strength */ +#define OCGPIO_CFG_OUT_STR_HIGH (((uint32_t) 2) << OCGPIO_CFG_OUT_STR_LSB) /*!< High drive strength */ + +/* + * OC GPIO I/O configuration settings - these are to be used by drivers that + * control GPIO pins. These are settings that are generic enough that every + * GPIO driver should support. These settings aren't layout specific, but are + * instead dictated by the higher level driver (see OcGpio_configure) + * ============================================================================ + */ +typedef union OcGpio_ioCfg { + struct { + uint32_t dir:1; + uint32_t default_val:1; + uint32_t int_cfg:3; + }; + uint32_t uint32; +} OcGpio_ioCfg; + +#define OCGPIO_CFG_DIR_BIT 0 +#define OCGPIO_CFG_OUT_BIT 1 +#define OCGPIO_CFG_INT_LSB 2 + +#define OCGPIO_CFG_OUTPUT (((uint32_t) 0) << OCGPIO_CFG_DIR_BIT) /*!< Pin is an output. */ +#define OCGPIO_CFG_INPUT (((uint32_t) 1) << OCGPIO_CFG_DIR_BIT) /*!< Pin is an input. */ + +#define OCGPIO_CFG_OUT_LOW (((uint32_t) 0) << OCGPIO_CFG_OUT_BIT) /*!< Output low by default */ +#define OCGPIO_CFG_OUT_HIGH (((uint32_t) 1) << OCGPIO_CFG_OUT_BIT) /*!< Output high by default */ + +#define OCGPIO_CFG_INT_NONE (((uint32_t) 0) << OCGPIO_CFG_INT_LSB) /*!< No Interrupt */ +#define OCGPIO_CFG_INT_FALLING (((uint32_t) 1) << OCGPIO_CFG_INT_LSB) /*!< Interrupt on falling edge */ +#define OCGPIO_CFG_INT_RISING (((uint32_t) 2) << OCGPIO_CFG_INT_LSB) /*!< Interrupt on rising edge */ +#define OCGPIO_CFG_INT_BOTH_EDGES (((uint32_t) 3) << OCGPIO_CFG_INT_LSB) /*!< Interrupt on both edges */ +#define OCGPIO_CFG_INT_LOW (((uint32_t) 4) << OCGPIO_CFG_INT_LSB) /*!< Interrupt on low level */ +#define OCGPIO_CFG_INT_HIGH (((uint32_t) 5) << OCGPIO_CFG_INT_LSB) /*!< Interrupt on high level */ + +/* Wrapper functions to dispatch call to appropriate v-table + * ================================================================== + */ + +/* Since this is simply an interface definition, define the functions in the + * header to allow them to be inlined for better efficiency. Any functions added + * to this module that are more than a simple wrapper should be added to + * OcGpio.c instead. + */ + +/*! Probe the device for POStT + * probe function + * @param pin OcGPio_Port pointer to the driver instance to find the device + * @return 0 on success, negative on failure + */ +static inline int OcGpio_probe(const OcGpio_Port *port) { + if( port && port->fn_table && port->fn_table->probe) { + return port->fn_table->probe(port); + } else { + return OCGPIO_FAILURE; + } +} + +/*! Initialize the port - tempted to remove in favor of more generic device + * init function + * @param pin OcGPio_Port pointer to the driver instance to initialize + * @return 0 on success, negative on failure + */ +static inline int OcGpio_init(const OcGpio_Port *port) { + if( port && port->fn_table && port->fn_table->init) { + return port->fn_table->init(port); + } else { + return OCGPIO_FAILURE; + } +} + +/*! Write a value to a GPIO pin + * @param pin OcGPio_Pin pointer for the pin to be controlled + * @param value Boolean value to write to the pin + * @return 0 on success, negative on failure + */ +static inline int OcGpio_write(const OcGpio_Pin *pin, bool value) { + if( pin && pin->port && pin->port->fn_table && + pin->port->fn_table->write) { + return pin->port->fn_table->write(pin, value); + } else { + return OCGPIO_FAILURE; + } +} + +/*! Read a value from a GPIO pin + * @param pin OcGPio_Pin pointer for the pin to read + * @return Boolean value of pin, or negative if failure + */ +static inline int OcGpio_read(const OcGpio_Pin *pin) { + if( pin && pin->port && pin->port->fn_table && + pin->port->fn_table->read) { + return pin->port->fn_table->read(pin); + } else { + return OCGPIO_FAILURE; + } +} + +/*! Configure a GPIO pin's parameters (both io and hw params) + * @note This must be called before using the pin + * @param pin OcGPio_Pin pointer to the pin to configure + * @param cfg Bitfield of OCGPIO_CFG io values (direction, interrupt edge) + * @return 0 on success, negative on failure + */ +static inline int OcGpio_configure(const OcGpio_Pin *pin, uint32_t cfg) { + if( pin && pin->port && pin->port->fn_table && + pin->port->fn_table->configure) { + return pin->port->fn_table->configure(pin, cfg); + } else { + return OCGPIO_FAILURE; + } +} + +/*! Add a callback subscriber to an interrupt-enabled pin + * @note Multiple callbacks can be subscribed to a single pin + * @param pin OcGPio_Pin pointer to subscribe to changes on + * @param callback Function to call when interrupt is triggered + * @param context Context pointer that is passed to callback function + * @return 0 on success, negative on failure + */ +static inline int OcGpio_setCallback (const OcGpio_Pin *pin, + OcGpio_CallbackFn callback, + void *context) { + if( pin && pin->port && pin->port->fn_table && + pin->port->fn_table->setCallback) { + return pin->port->fn_table->setCallback(pin, callback, context); + } else { + return OCGPIO_FAILURE; + } +} + +/*! Disable pin interrupt + * @param pin OcGPio_Pin pointer for the pin to disable the interrupt on + * @return 0 on success, negative on failure + */ +static inline int OcGpio_disableInt(const OcGpio_Pin *pin) { + if( pin && pin->port && pin->port->fn_table && + pin->port->fn_table->disableInt) { + return pin->port->fn_table->disableInt(pin); + } else { + return OCGPIO_FAILURE; + } +} + +/*! Enable pin interrupt + * @note This must be called after OcGpio_setCallback in order to activate the + * interrupt + * @param pin OcGPio_Pin pointer for the pin to enable the interrupt on + * @return 0 on success, negative on failure + */ +static inline int OcGpio_enableInt(const OcGpio_Pin *pin) { + if( pin && pin->port && pin->port->fn_table && + pin->port->fn_table->enableInt) { + return pin->port->fn_table->enableInt(pin); + } else { + return OCGPIO_FAILURE; + } +} + +#endif /* _OCGPIO_H_ */ diff --git a/firmware/psu/src/helpers/array.h b/firmware/psu/src/helpers/array.h new file mode 100644 index 0000000000..a65c7074ed --- /dev/null +++ b/firmware/psu/src/helpers/array.h @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#pragma once + +#ifndef HELPERS_ARRAY_H_ +#define HELPERS_ARRAY_H_ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +#endif /* HELPERS_ARRAY_H_ */ diff --git a/firmware/psu/src/helpers/attribute.h b/firmware/psu/src/helpers/attribute.h new file mode 100644 index 0000000000..380ce47aee --- /dev/null +++ b/firmware/psu/src/helpers/attribute.h @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#pragma once + +#ifndef HELPERS_ATTRIBUTE_H_ +#define HELPERS_ATTRIBUTE_H_ + +#define PACKED __attribute__ ((__packed__)) + +#define UNUSED(x) (void)(x) + +#endif /* HELPERS_ATTRIBUTE_H_ */ diff --git a/firmware/psu/src/helpers/math.h b/firmware/psu/src/helpers/math.h new file mode 100644 index 0000000000..4499d13ee4 --- /dev/null +++ b/firmware/psu/src/helpers/math.h @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#pragma once + +#ifndef HELPERS_MATH_H_ +#define HELPERS_MATH_H_ + +#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) +#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y)) + +#define CONSTRAIN(x, min, max) MIN(MAX((x), (min)), (max)) + +#endif /* HELPERS_MATH_H_ */ diff --git a/firmware/psu/src/helpers/memory.c b/firmware/psu/src/helpers/memory.c new file mode 100644 index 0000000000..20260a96d8 --- /dev/null +++ b/firmware/psu/src/helpers/memory.c @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "memory.h" + +#include "inc/common/global_header.h" + +void printMemory(const void *start, size_t size) { + for (size_t i = 0; i < size; ++i) { + DEBUG("0x%02x ", ((uint8_t *)start)[i]); + + if ((i+1) % 8 == 0) { + DEBUG("\n"); + } else if ((i+1) % 4 == 0) { + DEBUG(" "); + } + } + DEBUG("\n"); +} + +uint8_t set_bit8(uint8_t byte, uint8_t bit, bool value) { + const uint8_t mask = 1 << bit; + if (value) { + byte |= mask; + } else { + byte &= ~mask; + } + return byte; +} diff --git a/firmware/psu/src/helpers/memory.h b/firmware/psu/src/helpers/memory.h new file mode 100644 index 0000000000..a24c30e312 --- /dev/null +++ b/firmware/psu/src/helpers/memory.h @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#pragma once + +#ifndef HELPERS_MEMORY_H_ +#define HELPERS_MEMORY_H_ + +#include +#include +#include + +#ifndef HIWORD + #define HIWORD(x) ((uint16_t)((x) >> 16)) +#endif + +#ifndef LOWORD + #define LOWORD(x) ((uint16_t)(x)) +#endif + +#ifndef HIBYTE + #define HIBYTE(x) ((uint8_t)((x) >> 8)) +#endif + +#ifndef LOBYTE + #define LOBYTE(x) ((uint8_t)(x)) +#endif + +#define zalloc(size) calloc((size), 1) + +void printMemory(const void *start, size_t size); + +/* Sets a bit in a uint8 to the specified value (1/0) + * @param byte The original value we're modifying + * @param bit The index of the bit we want to set/clear + * @param value The value the bit should be set to (1/0) + * @return The modified byte with the bit's new value + */ +uint8_t set_bit8(uint8_t byte, uint8_t bit, bool value); + +#endif /* HELPERS_MEMORY_H_ */ diff --git a/firmware/psu/src/interfaces/UART/uartdma.c b/firmware/psu/src/interfaces/UART/uartdma.c new file mode 100644 index 0000000000..eec50cccb8 --- /dev/null +++ b/firmware/psu/src/interfaces/UART/uartdma.c @@ -0,0 +1,517 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** +#include "comm/gossiper.h" +#include "common/inc/global/ocmp_frame.h" +#include "inc/interfaces/uartdma.h" +#include "inc/common/global_header.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +char input[64]; +UART_Handle uart; + +/***************************************************************************** + * HANDLES DEFINITION + *****************************************************************************/ +/* Semaphore */ +Semaphore_Handle semUART; +Semaphore_Handle semUARTTX; + +/* Queue object */ +Queue_Struct uartRxMsg; +Queue_Struct uartTxMsg; +Queue_Handle uartRxMsgQueue; +Queue_Handle uartTxMsgQueue; + +/* Global Task Configuration Variables */ +Task_Struct ocUARTDMATask; +Char ocUARTDMATaskStack[OCUARTDMA_TASK_STACK_SIZE]; + +Task_Struct ocUARTDMATxTask; +Char ocUARTDMATxTaskStack[OCUARTDMATX_TASK_STACK_SIZE]; + +/***************************************************************************** + * The transmit and receive buffers used for the UART transfers. There is one + * transmit buffer and a pair of recieve ping-pong buffers. + ******************************************************************************/ +static uint8_t ui8TxBuf[UART_TXBUF_SIZE]; +static uint8_t ui8RxBufA[UART_RXBUF_SIZE]; +static uint8_t ui8RxBufB[UART_RXBUF_SIZE]; +static uint8_t ui8uartdmaRxBuf[UART_RXBUF_SIZE]; + +/***************************************************************************** + * The control table used by the uDMA controller. This table must be aligned + * to a 1024 byte boundary. + *****************************************************************************/ +#pragma DATA_ALIGN(pui8ControlTable, 1024) + +uint8_t pui8ControlTable[1024]; + +/***************************************************************************** + * + * The interrupt handler for uDMA errors. This interrupt will occur if the + * uDMA encounters a bus error while trying to perform a transfer. This + * handler just increments a counter if an error occurs. + *****************************************************************************/ +void uDMAErrorHandler(void) +{ + uint32_t ui32Status; + + /* Check for uDMA error bit. */ + ui32Status = uDMAErrorStatusGet(); + + /* If there is a uDMA error, then clear the error and increment the error + * counter.*/ + if (ui32Status) { + uDMAErrorStatusClear(); + LOGGER_WARNING("UARTDMACTR:WARNING::Something went bad in uDMA.\n"); + } +} + +/***************************************************************************** + * + * The interrupt handler for UART4. + * + *****************************************************************************/ +void UART4IntHandler(void) +{ + uint32_t ui32Status; + uint32_t ui32Mode; + ui32Status = UARTIntStatus(UART4_BASE, 1); + + /* Clear any pending status*/ + UARTIntClear(UART4_BASE, ui32Status); + + /*Primary Buffer*/ + ui32Mode = uDMAChannelModeGet(UDMA_CHANNEL_TMR0A | UDMA_PRI_SELECT); + if (ui32Mode == UDMA_MODE_STOP) { + uDMAChannelTransferSet(UDMA_CHANNEL_TMR0A | UDMA_PRI_SELECT, + UDMA_MODE_PINGPONG, + (void *)(UART4_BASE + UART_O_DR), + ui8RxBufA, sizeof(ui8RxBufA)); + /*Preparing message to send to UART RX Queue*/ + memset(ui8uartdmaRxBuf,'\0',UART_RXBUF_SIZE); + memcpy(ui8uartdmaRxBuf,ui8RxBufA,sizeof(ui8RxBufA)); + Semaphore_post(semUART); + } + + /*Alternate Buffer*/ + ui32Mode = uDMAChannelModeGet(UDMA_CHANNEL_TMR0A | UDMA_ALT_SELECT); + if(ui32Mode == UDMA_MODE_STOP) { + uDMAChannelTransferSet(UDMA_CHANNEL_TMR0A | UDMA_ALT_SELECT, + UDMA_MODE_PINGPONG, + (void *)(UART4_BASE + UART_O_DR), + ui8RxBufB, sizeof(ui8RxBufB)); + /*Preparing message to send to UART RX Queue*/ + memset(ui8uartdmaRxBuf,'\0',UART_RXBUF_SIZE); + memcpy(ui8uartdmaRxBuf,ui8RxBufB,sizeof(ui8RxBufB)); + Semaphore_post(semUART); + } +} + +/***************************************************************************** + * Reset and configure DMA and UART. + *****************************************************************************/ +void resetUARTDMA(void) +{ + LOGGER_WARNING("UARTDMACTR:WARNING::Configuring UART DMA again.....!!!\n"); + + // Configure UART.*/ + ConfigureUART(); + + /* Configure UART.*/ + uartdma_init(); + LOGGER("UARTDMACTR:INFO::Re-Configuring UART DMA again.....!!!\n"); +} +typedef enum DK_TM4C129X_UARTName { + DK_TM4C129X_UART0 = 0, + DK_TM4C129X_UART1, + DK_TM4C129X_UART2, + DK_TM4C129X_UART3, + DK_TM4C129X_UART4, + DK_TM4C129X_UARTCOUNT +} DK_TM4C129X_UARTName; +/***************************************************************************** + * Configure the UART and its pins. This must be called before UARTprintf(). + *****************************************************************************/ +void ConfigureUART(void) +{ + LOGGER_DEBUG("UARTDMACTR:INFO::Configuring UART interface for communication.\n"); + + UART_Params uartParams; + + /* Enable the GPIO Peripheral used by the UART.*/ + SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC); + + /* Enable UART4 */ + SysCtlPeripheralEnable(SYSCTL_PERIPH_UART4); + SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UART4); + + // SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1); + // SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UART1); + + /* Configure GPIO Pins for UART mode.*/ + GPIOPinConfigure(GPIO_PC4_U4RX); + GPIOPinConfigure(GPIO_PC5_U4TX); + GPIOPinTypeUART(GPIO_PORTC_BASE, GPIO_PIN_4 | GPIO_PIN_5); + + + // GPIOPinConfigure(GPIO_PC4_U1RX); + // GPIOPinConfigure(GPIO_PC5_U1TX); + // GPIOPinTypeUART(GPIO_PORTC_BASE, GPIO_PIN_4 | GPIO_PIN_5); + + UART_init(); + + /* Create a UART with data processing off. */ + UART_Params_init(&uartParams); + uartParams.writeDataMode = UART_DATA_BINARY; + uartParams.readDataMode = UART_DATA_BINARY; + uartParams.readReturnMode = UART_RETURN_FULL; + uartParams.readEcho = UART_ECHO_OFF; + uartParams.baudRate = 115200; + + uart = UART_open(DK_TM4C129X_UART4, &uartParams); + // uart = UART_open(DK_TM4C129X_UART1, &uartParams); + +} + +/**************************************************************************** + * + * Initializes the UART4 peripheral and sets up the TX and RX uDMA channels. + *****************************************************************************/ +void InitUART4Transfer(void) +{ + LOGGER_DEBUG("UARTDMACTR:INFO::Configuring UART interrupt and uDMA channel for communication to GPP.\n"); + uint_fast16_t ui16Idx; + const uint32_t SysClock = 120000000; + + /* TX buffer init to 0.*/ + for (ui16Idx = 0; ui16Idx < UART_TXBUF_SIZE; ui16Idx++) { + ui8TxBuf[ui16Idx] = 0; + } + + /* Enable the UART peripheral, and configure it to operate even if the CPU + * is in sleep.*/ + SysCtlPeripheralEnable(SYSCTL_PERIPH_UART4); + SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UART4); + + /* Configure the UART communication parameters.*/ + + UARTConfigSetExpClk(UART4_BASE, SysClock, 115200, + UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | + UART_CONFIG_PAR_NONE); + + /* Set both the TX and RX trigger thresholds to 4. */ + UARTFIFOLevelSet(UART4_BASE, UART_FIFO_TX4_8, UART_FIFO_RX4_8); + + /* Enable the UART for operation, and enable the uDMA interface for both TX + * and RX channels.*/ + UARTEnable(UART4_BASE); + UARTDMAEnable(UART4_BASE, UART_DMA_RX | UART_DMA_TX); + + uDMAChannelAttributeDisable(UDMA_CHANNEL_TMR0A, UDMA_ATTR_ALTSELECT + | UDMA_ATTR_USEBURST | UDMA_ATTR_HIGH_PRIORITY + | UDMA_ATTR_REQMASK); + + uDMAChannelControlSet(UDMA_CHANNEL_TMR0A | UDMA_PRI_SELECT, + UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | + UDMA_ARB_4); + + uDMAChannelControlSet(UDMA_CHANNEL_TMR0A | UDMA_ALT_SELECT, + UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | + UDMA_ARB_4); + + uDMAChannelTransferSet(UDMA_CHANNEL_TMR0A | UDMA_PRI_SELECT, + UDMA_MODE_PINGPONG, + (void *)(UART4_BASE + UART_O_DR), + ui8RxBufA, sizeof(ui8RxBufA)); + + uDMAChannelTransferSet(UDMA_CHANNEL_TMR0A | UDMA_ALT_SELECT, + UDMA_MODE_PINGPONG, + (void *)(UART4_BASE + UART_O_DR), + ui8RxBufB, sizeof(ui8RxBufB)); + + uDMAChannelAttributeDisable(UDMA_CHANNEL_TMR0B, + UDMA_ATTR_ALTSELECT | UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK); + + uDMAChannelAttributeEnable(UDMA_CHANNEL_TMR0B, UDMA_ATTR_USEBURST); + + uDMAChannelControlSet(UDMA_CHANNEL_TMR0B | UDMA_PRI_SELECT, + UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_4); + + uDMAChannelTransferSet(UDMA_CHANNEL_TMR0B | UDMA_PRI_SELECT, + UDMA_MODE_BASIC, ui8TxBuf, + (void *)(UART4_BASE + UART_O_DR), + sizeof(ui8TxBuf)); + + uDMAChannelAssign(UDMA_CH18_UART4RX); + uDMAChannelAssign(UDMA_CH19_UART4TX); + + uDMAChannelEnable(UDMA_CHANNEL_TMR0A); + uDMAChannelEnable(UDMA_CHANNEL_TMR0B); + + /* Enable the UART DMA TX/RX interrupts.*/ + UARTIntEnable(UART4_BASE, UART_INT_DMARX ); + + /* Enable the UART peripheral interrupts.*/ + IntEnable(INT_UART4); +} + + +/***************************************************************************** + * Intialize UART uDMA for the data transfer. This will initialise both Tx and + * Rx Channel associated with UART Tx and Rx + ****************************************************************************/ +void uartdma_init(void) +{ + LOGGER_DEBUG("UARTDMACTR:INFO::Starting uDMA intilaization.\n"); + SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA); + SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UDMA); + IntEnable(INT_UDMAERR); + uDMAEnable(); + uDMAControlBaseSet(pui8ControlTable); + InitUART4Transfer(); +} + +/***************************************************************************** + * Initialize the UART with DMA interface. + ****************************************************************************/ +void uartDMAinterface_init(void) +{ + /* Configure UART */ + ConfigureUART(); + + /* Initialize UART */ +// uartdma_init(); + + /*UART RX Semaphore */ + LOGGER_DEBUG("UARTDMACTR:INFO:: uartDMA interface intialization.\n"); + semUART = Semaphore_create(0, NULL, NULL); + if (semUART == NULL) { + LOGGER_ERROR("UARTDMACTR:ERROR::UART RX Semaphore creation failed.\n"); + } + + /*UART OCMP RX Message Queue*/ + uartRxMsgQueue = Util_constructQueue(&uartRxMsg); + LOGGER_DEBUG("UARTDMACTR:INFO::Constructing message Queue 0x%x for UART RX OCMP Messages.\n", + uartRxMsgQueue); + + LOGGER_DEBUG("UARTDMACTR:INFO::Waiting for OCMP UART RX messgaes....!!!\n"); +} + +/***************************************************************************** + * uartdma_rx_taskfxn -Handles the UART received data. + ****************************************************************************/ +static void uartdma_rx_taskfxn(UArg arg0, UArg arg1) +{ + // Initialize application + uartDMAinterface_init(); + + // Application main loop + while (true) { + UART_read(uart, &input, 64); + // if (Semaphore_pend(semUART, BIOS_WAIT_FOREVER)) { + if(1) { + /* Reset Uart DMA if the SOF is not equal to 0X55 */ + // if (ui8uartdmaRxBuf[0] != OCMP_MSG_SOF) { + if (input[0] != OCMP_MSG_SOF) { + // resetUARTDMA(); + } else { + /* OCMP UART RX Messgaes */ + uint8_t * pWrite = NULL; + pWrite = (uint8_t *) malloc( + sizeof(OCMPMessageFrame) + OCMP_FRAME_MSG_LENGTH); + if (pWrite != NULL) { + memset(pWrite, '\0', UART_RXBUF_SIZE); + memcpy(pWrite, input, UART_RXBUF_SIZE); +#if 0 + uint8_t i = 0; + LOGGER_DEBUG("UARTDMACTR:INFO:: UART RX BUFFER:\n"); + for( i = 0; i < UART_RXBUF_SIZE; i++) + { + LOGGER_DEBUG("0x%x ",input[i]); + } + LOGGER_DEBUG("\n"); +#endif + Util_enqueueMsg(gossiperRxMsgQueue, semGossiperMsg, pWrite); + } else { + LOGGER_ERROR("UARTDMACTR:ERROR:: No memory left for Msg Length %d.\n", + UART_RXBUF_SIZE); + } + } + } + } +} + +/***************************************************************************** + * uartdma_tx_taskinit - Creating IPC objects + *****************************************************************************/ +void uartdma_tx_taskinit(void) +{ + LOGGER_DEBUG("UARTDMACTR:INFO:: uartDMA interface intialization.\n"); + + /*UART TX Semaphore */ + semUARTTX = Semaphore_create(0, NULL, NULL); + if (semUARTTX == NULL) { + LOGGER_ERROR("UARTDMACTR:ERROR::UART TX Semaphore creation failed.\n"); + } + + /*UART OCMP TX Message Queue*/ + uartTxMsgQueue = Util_constructQueue(&uartTxMsg); + LOGGER_DEBUG("UARTDMACTR:INFO::Constructing message Queue 0x%x for UART TX OCMP Messages.\n", + uartTxMsgQueue); +} + + +/***************************************************************************** + ** FUNCTION NAME : uartdma_process_tx_message + ** + ** DESCRIPTION : transmitt TX Messages to UART physical medium + ** + ** ARGUMENTS : Pointer to UARTDMATX_Evt_t structure + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static ReturnStatus uartdma_process_tx_message(uint8_t *pMsg) +{ + ReturnStatus status = RETURN_OK; + if (1) { + memset(ui8TxBuf, '\0', UART_TXBUF_SIZE); + + memcpy(ui8TxBuf, pMsg, UART_TXBUF_SIZE); +#if 0 + uint8_t i = 0; + LOGGER_DEBUG("UARTDMACTR:INFO:: UART TX BUFFER:\n"); + for( i = 0; i < UART_TXBUF_SIZE; i++) + { + LOGGER_DEBUG("0x%x ",ui8TxBuf[i]); + } + LOGGER_DEBUG("\n"); +#endif + UART_write(uart, ui8TxBuf, UART_TXBUF_SIZE); + + } else { + status = RETURN_NOTOK; + } + return status; +} +/* +static ReturnStatus uartdma_process_tx_message(uint8_t *pMsg) +{ + ReturnStatus status = RETURN_OK; + if (!uDMAChannelIsEnabled(UDMA_CHANNEL_TMR0B)) { + memset(ui8TxBuf, '\0', UART_TXBUF_SIZE); + + memcpy(ui8TxBuf, pMsg, UART_TXBUF_SIZE); +#if 1 + uint8_t i = 0; + LOGGER_DEBUG("UARTDMACTR:INFO:: UART TX BUFFER:\n"); + for( i = 0; i < UART_TXBUF_SIZE; i++) + { + LOGGER_DEBUG("0x%x ",ui8TxBuf[i]); + } + LOGGER_DEBUG("\n"); +#endif + uDMAChannelTransferSet(UDMA_CHANNEL_TMR0B | UDMA_PRI_SELECT, + UDMA_MODE_BASIC, ui8TxBuf, + (void *) (UART4_BASE + UART_O_DR), sizeof(ui8TxBuf)); + uDMAChannelEnable(UDMA_CHANNEL_TMR0B); + } else { + status = RETURN_NOTOK; + } + return status; +} +*/ +/***************************************************************************** + * uartdma_tx_taskfxn - Handles the UART sent data. + ****************************************************************************/ +static void uartdma_tx_taskfxn(UArg arg0, UArg arg1) +{ + /* UARTDMA TX init*/ + uartdma_tx_taskinit(); + + // Application main loop + while (true) { + if (Semaphore_pend(semUARTTX, BIOS_WAIT_FOREVER)) { + /* OCMP UART TX Messgaes */ + while (!Queue_empty(uartTxMsgQueue)) { + uint8_t *pWrite = (uint8_t *) Util_dequeueMsg(uartTxMsgQueue); + if (pWrite) { + uartdma_process_tx_message(pWrite); + } + free(pWrite); + } + } + } +} + +/****************************************************************************** + ** FUNCTION NAME : uartdma_rx_createtask + ** + ** DESCRIPTION : Task creation function for the UARTDMA + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + ******************************************************************************/ +void uartdma_rx_createtask(void) +{ + Task_Params taskParams; + Task_Params_init(&taskParams); + taskParams.stackSize = OCUARTDMA_TASK_STACK_SIZE; + taskParams.stack = &ocUARTDMATaskStack; + taskParams.instance->name = "UART_DMA_TASK"; + taskParams.priority = OCUARTDMA_TASK_PRIORITY; + Task_construct(&ocUARTDMATask, (Task_FuncPtr) uartdma_rx_taskfxn, + &taskParams, NULL); + LOGGER_DEBUG("UARTDMACTRl:INFO::Creating UART DMA task function.\n"); +} + +/****************************************************************************** + ** FUNCTION NAME : uartdma_tx_createtask + ** + ** DESCRIPTION : Task creation function for the UARTDMA TX + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + ******************************************************************************/ +void uartdma_tx_createtask(void) +{ + Task_Params taskParams; + Task_Params_init(&taskParams); + taskParams.stackSize = OCUARTDMATX_TASK_STACK_SIZE; + taskParams.stack = &ocUARTDMATxTaskStack; + taskParams.instance->name = "UART_DMA_TX_TASK"; + taskParams.priority = OCUARTDMATX_TASK_PRIORITY; + Task_construct(&ocUARTDMATxTask, (Task_FuncPtr) uartdma_tx_taskfxn, + &taskParams, NULL); + LOGGER_DEBUG("UARTDMACTRl:INFO::Creating UART DMA TX task function.\n"); +} diff --git a/firmware/psu/src/main.c b/firmware/psu/src/main.c new file mode 100644 index 0000000000..dcee2f0e8e --- /dev/null +++ b/firmware/psu/src/main.c @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +//***************************************************************************** +// HEADER FILES +//***************************************************************************** +#include "Board.h" +#include "inc/common/bigbrother.h" +#include "inc/common/global_header.h" + +#include +#include + +#define xstr(a) str(a) +#define str(a) #a + +//***************************************************************************** +// FUNCTION DECLARATIONS +//***************************************************************************** +extern int ethernet_start(void); + +static void openCellular_init(void) +{ + LOGGER_DEBUG("|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n"); + LOGGER_DEBUG("|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n"); + LOGGER_DEBUG("|||| |||| |||| ||||| |||||| |||| |||| |||| ||||||||| ||||||||| |||| |||| ||||||||| |||| ||||\n"); + LOGGER_DEBUG("|||| |||| |||| |||| |||| |||||||||| | ||||| |||| ||||||||| ||||||||| ||||||||| ||||||||| |||| |||| ||||||||| |||| |||| |||| ||||\n"); + LOGGER_DEBUG("|||| |||| |||| |||| |||| |||||||||| || |||| |||| ||||||||| ||||||||| ||||||||| ||||||||| |||| |||| ||||||||| |||| |||| |||| ||||\n"); + LOGGER_DEBUG("|||| |||| |||| |||| ||||| ||| ||| |||| ||||||||| |||| ||||||||| ||||||||| |||| |||| ||||||||| |||| ||||\n"); + LOGGER_DEBUG("|||| |||| |||| |||||||||| |||||||||| |||| || |||| ||||||||| ||||||||| ||||||||| ||||||||| |||| |||| ||||||||| |||| |||| || ||||||\n"); + LOGGER_DEBUG("|||| |||| |||| |||||||||| |||||||||| ||||| | |||| ||||||||| ||||||||| ||||||||| ||||||||| |||| |||| ||||||||| |||| |||| ||| |||||\n"); + LOGGER_DEBUG("|||| |||| |||||||||| ||||| |||||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| ||||\n"); + LOGGER_DEBUG("|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n"); + LOGGER_DEBUG("|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n"); + LOGGER_DEBUG("\nOCWare v" + xstr(_FW_REV_MAJOR_)"." + xstr(_FW_REV_MINOR_)"." + xstr(_FW_REV_BUGFIX_)"-" + xstr(_FW_REV_TAG_)"\n"); + LOGGER_DEBUG("Build Date: "__DATE__" "__TIME__"\n\n"); +} + +static void exit_handler(int unused) +{ + /* Perform a full system reset if we fault, + * hope it doesn't happen again */ + SysCtlReset(); +} + +/*Main Function */ +int main(void) +{ + /* Install an exit handler to catch if we fault out */ +// OcGpio_Pin pin_watchdog = { &ec_io, OC_EC_WD_INPUT }; + + System_atexit(exit_handler); + openCellular_init(); + /* Call board init functions */ + Board_initGeneral(); + Board_initGPIO(); + Board_initI2C(); + +//TODO: PWR2 + // Board_initUART(); + bigbrother_createtask(); + /* Start BIOS */ + BIOS_start(); + return (0); +} diff --git a/firmware/psu/src/post/post.c b/firmware/psu/src/post/post.c new file mode 100644 index 0000000000..b697366aa7 --- /dev/null +++ b/firmware/psu/src/post/post.c @@ -0,0 +1,418 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** + +#include "comm/gossiper.h" +#include "common/inc/global/ocmp_frame.h" +#include "common/inc/global/post_frame.h" +#include "inc/common/bigbrother.h" +#include "inc/common/post.h" +#include "inc/subsystem/hci/hci.h" +#include "inc/subsystem/power/power.h" +#include "inc/utils/ocmp_util.h" +#include "registry/SSRegistry.h" + +#include + +#include +#include + +//***************************************************************************** +// HANDLES DEFINITION +//***************************************************************************** + +/* Queue object */ +/* + * Semaphore for the POST task where it will be waiting on + * sub systems or BigBrother postmessages. + */ +Semaphore_Handle semPOSTMsg; + +static Queue_Struct postRxMsg; + +/* + * postRxMsgQueue - Used by the BigBrother/Subsystem to pass the POST request + * and ack message. + */ +Queue_Handle postRxMsgQueue; + +/* Global Task Configuration Variables */ +static Task_Struct postTask; +static Char postTaskStack[POST_TASK_STACK_SIZE]; + +/* POST state */ +static uint8_t postState = 0; +static OCMPSubsystem POST_subSystem; +extern POSTData PostResult[POST_RECORDS]; + +/***************************************************************************** + * FUNCTION DECLARATIONS + *****************************************************************************/ +static void post_taskfxn(UArg a0, UArg a1); +static void post_task_init(void); +static ReturnStatus post_process_msg(OCMPSubsystem OC_subSystem); +static OCMPMessageFrame* post_create_execute_msg(OCMPSubsystem OC_subSystem); +static void post_activate(OCMPMessageFrame *pPOSTMsg); +static void post_process_rx_msg(OCMPMessageFrame *pPOSTMsg); +static void post_move_to_next_subsystem(); +static void post_update_result_to_bigbrother(OCMPMessageFrame *pPOSTMsg); +extern ReturnStatus bb_sys_post_complete(void); + +/***************************************************************************** + ** FUNCTION NAME : _post_complete + ** + ** DESCRIPTION : Get POST results from EEPROM. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void _post_complete() +{ + uint8_t iter = 0; + LOGGER_DEBUG("POST:INFO::POST test is completed.\n"); + LOGGER_DEBUG("||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n"); + LOGGER_DEBUG("|||||||||||||||||||||||||||||||||||||||||||||||||||||||POST TABLE|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n"); + /* POST results */ + for (iter = 0; iter < POST_RECORDS; iter++) { + LOGGER_DEBUG("\t POST:INFO:: POSTRESULT SS: 0x%x Device S.No: 0x%x I2C Bus: 0x%x Device Addr: 0x%x Device Id: 0x%x Manufacture Id: 0x%x Status: 0x%x.\n", + PostResult[iter].subsystem, PostResult[iter].devSno, + PostResult[iter].i2cBus, PostResult[iter].devAddr, + PostResult[iter].devId, PostResult[iter].manId, + PostResult[iter].status); + } + LOGGER_DEBUG("||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n"); + LOGGER_DEBUG("||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n"); +} + + +/***************************************************************************** + ** FUNCTION NAME : post_data_init + ** + ** DESCRIPTION : Initializes post struct. + ** + ** ARGUMENTS : subsystem , device serial number + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void post_init_POSTData(POSTData *pData,OCMPSubsystem subsystem, uint8_t devSno) +{ + pData->subsystem = subsystem; + pData->devSno = devSno; + pData->i2cBus = 0xFF; + pData->devAddr = 0xFF; + pData->manId = 0xFFFF; + pData->devId = 0xFFFF; + pData->status = POST_DEV_MISSING; +} + +/***************************************************************************** + ** FUNCTION NAME : post_update_deviceInfo + ** + ** DESCRIPTION : Update bus, device address, manufacturing ID and device ID in post struct.\ + ** if no I2C bus is associated with device than it will be updated to 0xFF. + ** + ** ARGUMENTS : I2C Bus, Address, man Id, device Id. + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void post_update_POSTData(POSTData *pData, uint8_t I2CBus, uint8_t devAddress, uint16_t manId, uint16_t devId) +{ + pData->i2cBus = I2CBus; + pData->devAddr = devAddress; + pData->manId = manId; + pData->devId = devId; +} + +/***************************************************************************** + ** FUNCTION NAME : post_update_deviceStatus + ** + ** DESCRIPTION : Update post status + ** + ** ARGUMENTS : POSTData and status. + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void post_update_POSTStatus(POSTData *pData, ePostCode status) +{ + pData->status = status; +} + +/***************************************************************************** + ** FUNCTION NAME : post_move_to_next_subsystem + ** + ** DESCRIPTION : Move to next subssytem in the OCSubSystem. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void post_move_to_next_subsystem() +{ + POST_subSystem = (OCMPSubsystem) (POST_subSystem + (OCMPSubsystem) 1); + if (POST_subSystem > OC_SS_MAX_LIMIT) { + POST_subSystem = OC_SS_PWR; + } +} + +/***************************************************************************** + ** FUNCTION NAME : post_update_result_to_bigbrother + ** + ** DESCRIPTION : Send POST completion status to Bigbrother. + ** + ** ARGUMENTS : OCMPMessageFrame pointer for the update status + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void post_update_result_to_bigbrother(OCMPMessageFrame *pPOSTMsg) +{ + pPOSTMsg->message.subsystem = OC_SS_PWR; //OC_SUBSYSTEM_MAX_LIMIT subsystem number taken for bigbrother + memcpy((pPOSTMsg->message.ocmp_data), &postState, 1); + Util_enqueueMsg(bigBrotherRxMsgQueue, semBigBrotherMsg, + (uint8_t*) pPOSTMsg); +} + +/***************************************************************************** + ** FUNCTION NAME : post_process_msg + ** + ** DESCRIPTION : Forward excecute POST message to subsystem. + ** + ** ARGUMENTS : Subsystem + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static ReturnStatus post_process_msg(OCMPSubsystem OC_subSystem) +{ + ReturnStatus status = RETURN_OK; + if (OC_subSystem == OC_SS_MAX_LIMIT) { + _post_complete(); + POST_subSystem = OC_SS_PWR; + } else { + OCMPMessageFrame *postFrame = post_create_execute_msg(OC_subSystem); + if (postFrame) { + if (!SSRegistry_sendMessage(OC_subSystem, postFrame)) { + LOGGER_DEBUG("POST:ERROR::Subsystem %d does not exist\n", + OC_subSystem); + } + } else { + LOGGER_DEBUG("POST:ERROR::Out of memory.\n"); + status = RETURN_NOTOK; + } + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : post_create_execute_msg + ** + ** DESCRIPTION : create execute POST message for subsystem. + ** + ** ARGUMENTS : Subsystem + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static OCMPMessageFrame* post_create_execute_msg(OCMPSubsystem OC_subSystem) +{ + LOGGER_DEBUG("POST:INFO::Activation POST for SS %d.",OC_subSystem); + OCMPMessageFrame *postExeMsg = create_ocmp_msg_frame(OC_subSystem, + OCMP_MSG_TYPE_POST, + OCMP_AXN_TYPE_ACTIVE, + 0x00,0x00,1); + return postExeMsg; +} + +/***************************************************************************** + ** FUNCTION NAME : post_create_enable_msg + ** + ** DESCRIPTION : create execute POST message for subsystem. + ** + ** ARGUMENTS : Subsystem + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static OCMPMessageFrame* post_create_enable_msg(OCMPSubsystem OC_subSystem) +{ + uint8_t dummyByte = 0xff; + OCMPActionType actionType = OCMP_AXN_TYPE_ENABLE; + LOGGER_DEBUG("POST:INFO::Enabling system for POST."); + if(OC_subSystem == OC_SS_MAX_LIMIT) { + OC_subSystem = OC_SS_PWR; + actionType = OCMP_AXN_TYPE_REPLY; + } else { + actionType = OCMP_AXN_TYPE_ENABLE; + } + OCMPMessageFrame *postExeMsg = create_ocmp_msg_frame(OC_subSystem, + OCMP_MSG_TYPE_POST, + actionType,0x00,0x00,1); + return postExeMsg; +} + + +/***************************************************************************** + ** FUNCTION NAME : post_activate + ** + ** DESCRIPTION : Processes the POST Acks received from the subssystems. + ** + ** ARGUMENTS : Pointer to POST_AckEvt_t structure + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void post_activate(OCMPMessageFrame *pPOSTMsg) +{ + ReturnStatus POSTAckstatus = RETURN_NOTOK; + LOGGER_DEBUG("POST:INFO:: Processing POST Ack received from the " + "Subsystem %d.\n",POST_subSystem); + System_flush(); + //Do the casting for the pMsg + //POSTAckstatus = (ReturnStatus) (pPOSTMsg->message.ocmp_data); + memcpy(&POSTAckstatus, pPOSTMsg->message.ocmp_data, 1); + if ( (pPOSTMsg->message.subsystem == OC_SS_PWR) + && (pPOSTMsg->message.action == OCMP_AXN_TYPE_ACTIVE) ){ + post_process_msg(POST_subSystem); + } else { + if (pPOSTMsg->message.subsystem == POST_subSystem) { + postState = (!POSTAckstatus) & postState; + LOGGER_ERROR("POST:INFO:: POST status for 0x%x Subsystem is 0x%x" + " and OC POST status is 0x%x.\n", + POST_subSystem, POSTAckstatus, postState); + if (pPOSTMsg) { + free(pPOSTMsg); + } + } + post_move_to_next_subsystem(); + post_process_msg(POST_subSystem); + } +} + +/***************************************************************************** + ** FUNCTION NAME : post_process_rx_msg + ** + ** DESCRIPTION : Processes the POST Acks received from the subssystems. + ** + ** ARGUMENTS : Pointer to POST_AckEvt_t structure + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void post_process_rx_msg(OCMPMessageFrame *pPOSTMsg) +{ + LOGGER_DEBUG("POST:INFO::Processing POST messages.\n"); + switch (pPOSTMsg->message.action) { + case OCMP_AXN_TYPE_ACTIVE: + case OCMP_AXN_TYPE_REPLY: + post_activate(pPOSTMsg); + bb_sys_post_complete(); + break; + default: + { + LOGGER_ERROR("POST::ERROR::Unkown action type 0x%x for POST" + " message.\n", pPOSTMsg->message.action); + /*TODO: Return POST fail to BB*/ + } + } +} + +/***************************************************************************** + ** FUNCTION NAME : post_task_init + ** + ** DESCRIPTION : Initializes the POST task. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void post_task_init(void) +{ + /*Creating Semaphore for RX Message Queue*/ + semPOSTMsg = Semaphore_create(0, NULL, NULL); + if (semPOSTMsg == NULL) { + LOGGER_ERROR("POST:ERROR::POST RX Semaphore creation failed.\n"); + } + /*Creating RX Message Queue*/ + postRxMsgQueue = Util_constructQueue(&postRxMsg); + LOGGER_DEBUG("POST:INFO::Constructing message Queue for 0x%x POST RX Messages.\n", + postRxMsgQueue); + + /* Reset POST state to fail */ + postState = 0; + POST_subSystem = OC_SS_PWR; + OCMPMessageFrame *postEnableMsg = create_ocmp_msg_frame(OC_SS_PWR, + OCMP_MSG_TYPE_POST, + OCMP_AXN_TYPE_ACTIVE, + 0x00, + 0x00, + 1); + /*Ask for activate permission from BB system*/ + if (postEnableMsg) { + Util_enqueueMsg(bigBrotherRxMsgQueue, semBigBrotherMsg, + (uint8_t*) postEnableMsg); + } +} + +/****************************************************************************** + ** FUNCTION NAME : post_taskfxn + ** + ** DESCRIPTION : Executes POST test for Open cellular. + ** + ** ARGUMENTS : a0, a1 - not used + ** + ** RETURN TYPE : None + ** + ******************************************************************************/ +static void post_taskfxn(UArg a0, UArg a1) +{ + post_task_init(); + while (true) { + if (Semaphore_pend(semPOSTMsg, BIOS_WAIT_FOREVER)) { + while (!Queue_empty(postRxMsgQueue)) { + OCMPMessageFrame *pWrite = (OCMPMessageFrame *) Util_dequeueMsg( + postRxMsgQueue); + if (pWrite) { + post_process_rx_msg(pWrite); + } + } + } + } +} + +/******************************************************************************* + ** FUNCTION NAME : post_createtask + ** + ** DESCRIPTION : Task creation function for the POST. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *******************************************************************************/ +void post_createtask(void) +{ + Task_Params taskParams; + + // Configure task + Task_Params_init(&taskParams); + taskParams.stack = postTaskStack; + taskParams.stackSize = POST_TASK_STACK_SIZE; + taskParams.priority = OC_POST_TASKPRIORITY; + Task_construct(&postTask, post_taskfxn, &taskParams, NULL); +} diff --git a/firmware/psu/src/post/post_util.c b/firmware/psu/src/post/post_util.c new file mode 100644 index 0000000000..d967f3e209 --- /dev/null +++ b/firmware/psu/src/post/post_util.c @@ -0,0 +1,227 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "inc/common/post_util.h" +#include "inc/common/post.h" +#include "inc/common/system_states.h" +#include "platform/oc-sdr/schema/schema.h" +#include "src/registry/SSRegistry.h" + +extern const Component sys_schema[OC_SS_MAX_LIMIT]; +extern OCSubsystem *ss_reg[SUBSYSTEM_COUNT]; +POSTData PostResult[POST_RECORDS] = { { 0xFF } }; +static uint8_t deviceCount = 0; + +#ifdef UT_POST +/* + * TODO: Duplicating the definition of the following three functions from post.c for the UT framework + * If we include post.c in the UT framework , we are exposing a lot of OS dependent APIs like create_task , + * util_queue etc to the Windows Cygwin environment which will create linking issues. + * This will get fixed as part of #419 + */ + +void post_update_POSTStatus(POSTData *pData, ePostCode status) +{ + pData->status = status; +} + +void post_init_POSTData(POSTData *pData,OCMPSubsystem subsystem, uint8_t devSno) +{ + pData->subsystem = subsystem; + pData->devSno = devSno; + pData->i2cBus = 0xFF; + pData->devAddr = 0xFF; + pData->manId = 0xFFFF; + pData->devId = 0xFFFF; + pData->status = POST_DEV_MISSING; +} + +void post_update_POSTData(POSTData *pData, uint8_t I2CBus, uint8_t devAddress, uint16_t manId, uint16_t devId) +{ + pData->i2cBus = I2CBus; + pData->devAddr = devAddress; + pData->manId = manId; + pData->devId = devId; +} +#else +/* */ +/***************************************************************************** + ** FUNCTION NAME : _postDriver + ** + ** DESCRIPTION : Execute POST for a given device driver + ** (performs deep copy of alert_data) + ** + ** ARGUMENTS : pointer to subsytem, device, alert config + ** post data config and subsystem config + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _postDriver(const Component *subsystem, + const Component *dev, + const AlertData *alert_data, + POSTData* postData, OCSubsystem *ss) +{ +#if 0 + if (!dev->driver) { + return POST_DEV_NO_DRIVER_EXIST; + } +#endif + ePostCode postcode = POST_DEV_FOUND; + if (dev->driver->fxnTable->cb_probe) { + postcode = dev->driver->fxnTable->cb_probe(dev->driver_cfg,postData); + post_update_POSTStatus(postData, postcode); + } + LOGGER_DEBUG("%s:INFO:: %s (%s) %s\n", subsystem->name, + dev->name, dev->driver->name, + (postcode == POST_DEV_FOUND) ? "found" : "not found"); + + if (postcode == POST_DEV_FOUND) { + if (ss->state == SS_STATE_INIT) { + if (dev->driver->fxnTable->cb_init) { + AlertData *alert_data_cp = malloc(sizeof(AlertData)); + *alert_data_cp = *alert_data; + postcode = dev->driver->fxnTable->cb_init(dev->driver_cfg, + dev->factory_config, + alert_data_cp); + } else { + postcode = POST_DEV_NO_CFG_REQ; + } + post_update_POSTStatus(postData, postcode); + LOGGER_DEBUG("%s:INFO:: Configuration for %s (%s) is %s\n", + subsystem->name, + dev->name, + dev->driver->name, + (postcode == POST_DEV_CFG_DONE) ? "ok":(postcode == POST_DEV_NO_CFG_REQ) ? "not required." : "failed."); + } + } +} + +/***************************************************************************** + ** FUNCTION NAME : OCMP_mallocFrame + ** + ** DESCRIPTION : API to allocate an OCMP frame of a given data length + ** + ** ARGUMENTS : size of the payload of frame + ** + ** RETURN TYPE : OCMPMessageFrame + ** + *****************************************************************************/ +ReturnStatus _execPost(OCMPMessageFrame *pMsg, + unsigned int subsystem_id) +{ + const Component *subsystem = &sys_schema[subsystem_id]; + OCSubsystem *ss = ss_reg[subsystem_id]; + /* TODO: this is messy and assumes we have a pointer to the subsystem - + * we'll want to change this once the framework is more mature */ + if (ss->state == SS_STATE_PWRON) { + ss->state = SS_STATE_INIT; + } + + /* Iterate over each component & device within the subsystem, calling + * its post callback */ + ReturnStatus status = RETURN_OK; + if((subsystem->ssHookSet)&& + (ss->state == SS_STATE_INIT)) { + if(subsystem->ssHookSet->preInitFxn) { + if (!(subsystem->ssHookSet->preInitFxn(subsystem->driver_cfg, &(ss->state)))) { + status = RETURN_NOTOK; + return status; + } + } + } + POSTData postData; + ePostCode postcode = POST_DEV_MISSING; + uint8_t devSno = 0; + const Component *comp = &subsystem->components[0]; + for (uint8_t comp_id = 0; (comp && comp->name); ++comp, ++comp_id) { /* Component level (ec, ap, ch1, etc.) */ + /* If we have a driver at the component level, init */ + AlertData alert_data = { + .subsystem = (OCMPSubsystem)subsystem_id, + .componentId = comp_id, + .deviceId = 0, + }; + if(!comp->components) { + if (comp->postDisabled == POST_DISABLED ) { + continue ; + } + devSno++; + post_init_POSTData(&postData,subsystem_id,devSno); + //TODO: If postcode is configuration failure what should beth recovery action. + if (_postDriver(subsystem, comp, &alert_data, &postData, ss) == POST_DEV_NO_DRIVER_EXIST) { + devSno--; + } else { + post_update_POSTresult(&postData); + } + } else { + const Component *dev = &comp->components[0]; + for (uint8_t dev_id = 0; (dev && dev->name); ++dev, ++dev_id) { /* Device level (ts, ina, etc) */ + AlertData alert_data = { + .subsystem = (OCMPSubsystem)subsystem_id, + .componentId = comp_id, + .deviceId = dev_id, + }; + if(dev->postDisabled == POST_DISABLED ) { + continue; + } + devSno++; + post_init_POSTData(&postData,subsystem_id,devSno); + if(_postDriver(subsystem, dev, &alert_data, &postData, ss) == POST_DEV_NO_DRIVER_EXIST) { + devSno--; + } else { + post_update_POSTresult(&postData); + } + } + } + } + if((subsystem->ssHookSet)&&(ss->state == SS_STATE_INIT)) { + if(subsystem->ssHookSet->postInitFxn){ + if (!(subsystem->ssHookSet->postInitFxn(subsystem->driver_cfg, &(ss->state)))) { + ss->state = SS_STATE_FAULTY; + } + } + } + if (ss->state == SS_STATE_INIT && status == RETURN_OK) { + ss->state = SS_STATE_CFG; + } else { + ss->state = SS_STATE_FAULTY; + } + + LOGGER("%s:INFO:: Modules and sensors are %s.\n", subsystem->name, + ((status == RETURN_OK) ? "initialized." : "not initialized.")); + return status; +} + + +/* ***************************************************************************** + ** FUNCTION NAME : post_update_POSTresult + ** + ** DESCRIPTION : save post result to flash + ** + ** ARGUMENTS : a0, a1 - not used + ** + ** RETURN TYPE : None + ** + ******************************************************************************/ +void post_update_POSTresult(POSTData *postData) +{ + + /* Write a device info to flash but use a dummy function for REV B boards.*/ + uint8_t iter = 0; + /* Dump structure at particular location*/ + if ( (postData->subsystem == OC_SS_PWR) && (postData->devSno == 1 ) ) { + deviceCount = 0; + memset(PostResult, '\0', (POST_RECORDS * sizeof(POSTData))); + } else { + deviceCount++; + } + //LOGGER_DEBUG("POST:INFO:: Updating POST results for the Subsystem %d , Device Serial offset %d , Total Number of records %d.\n", + // postData->subsystem,postData->devSno,deviceCount+1); + memcpy(&PostResult[deviceCount],postData,sizeof(POSTData)); +} +#endif diff --git a/firmware/psu/src/registry/SSRegistry.c b/firmware/psu/src/registry/SSRegistry.c new file mode 100644 index 0000000000..27c2893e7c --- /dev/null +++ b/firmware/psu/src/registry/SSRegistry.c @@ -0,0 +1,662 @@ +/** +* Copyright (c) 2017-present, Facebook, Inc. +* All rights reserved. +* +* This source code is licensed under the BSD-style license found in the +* LICENSE file in the root directory of this source tree. An additional grant +* of patent rights can be found in the PATENTS file in the same directory. +*/ + +#include "common/inc/global/Framework.h" +#include "helpers/array.h" +#include "inc/common/bigbrother.h" /* For sending msg back via BB */ +#include "inc/common/post.h" +#include "inc/common/post_util.h" /* For sending POST response */ +#include "inc/common/global_header.h" +#include "inc/utils/ocmp_util.h" +#include "inc/utils/util.h" +#include "platform/oc-sdr/schema/schema.h" +#include "SSRegistry.h" + +#include + +#define OCMP_ACTION_TYPE_GET 1 +#define OCMP_ACTION_TYPE_SET 2 +#define OCMP_ACTION_TYPE_REPLY 3 +#define OCMP_ACTION_TYPE_ACTIVE 4 +#define OCMP_ACTION_TYPE_UPDATE 5 + +/* TODO: configurable directory (allow us to target different platforms) */ + +#define OC_TASK_STACK_SIZE 1000 +#define OC_TASK_PRIORITY 2 + +static char OC_task_stack[SUBSYSTEM_COUNT][OC_TASK_STACK_SIZE]; + +extern const Component sys_schema[SUBSYSTEM_COUNT]; + +OCSubsystem *ss_reg[SUBSYSTEM_COUNT] = {}; + +static const size_t PARAM_SIZE_MAP[] = { + [TYPE_NULL] = 0, + [TYPE_INT8] = sizeof(int8_t), + [TYPE_UINT8] = sizeof(uint8_t), + [TYPE_INT16] = sizeof(int16_t), + [TYPE_UINT16] = sizeof(uint16_t), + [TYPE_INT32] = sizeof(int32_t), + [TYPE_UINT32] = sizeof(uint32_t), + [TYPE_INT64] = sizeof(int64_t), + [TYPE_UINT64] = sizeof(uint64_t), + [TYPE_STR] = 1, /* TODO: properly handle strings */ + [TYPE_BOOL] = sizeof(bool), + [TYPE_ENUM] = 1, /* TODO: this really depends on enum - param_size should + iterate over definition to determine size requirement*/ +}; + +/***************************************************************************** + ** FUNCTION NAME : _subcompCount + ** + ** DESCRIPTION : API to calculate number of components + ** + ** ARGUMENTS : pointer to the component structure + ** + ** RETURN TYPE : int + ** + *****************************************************************************/ +static unsigned int _subcompCount(const Component *comp) { + unsigned int i = 0; + if (comp) { + while (comp->components[i].name) { + ++i; + } + } + return i; +} + +/***************************************************************************** + ** FUNCTION NAME : _paramSize + ** + ** DESCRIPTION : API to calculate size of parameter from schema + ** + ** ARGUMENTS : pointer to the parameter structure + ** + ** RETURN TYPE : size_t + ** + *****************************************************************************/ +static size_t _paramSize(const Parameter *param) { + if (!param || (param->type >= ARRAY_SIZE(PARAM_SIZE_MAP))) { + return 0; + } + + if (param->type == TYPE_STR) { + return param->size; + } + return PARAM_SIZE_MAP[param->type]; +} + +/***************************************************************************** + ** FUNCTION NAME : _compIsValid + ** + ** DESCRIPTION : API to check if component is valid + ** + ** ARGUMENTS : pointer to component + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _compIsValid(const Component *comp) +{ + return comp && comp->name; +} + +/***************************************************************************** + ** FUNCTION NAME : _paramIsValid + ** + ** DESCRIPTION : API to check if the parameter is valid + ** + ** ARGUMENTS : pointer to parameter + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _paramIsValid(const Parameter *param) +{ + return param && param->name; +} + +/***************************************************************************** + ** FUNCTION NAME : OCMP_mallocFrame + ** + ** DESCRIPTION : alert handler at the SS registry level + ** + ** ARGUMENTS : pointer to alert_data , alert id , pointer to payload + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void OCMP_GenerateAlert(const AlertData *alert_data, + unsigned int alert_id, + const void *data) +{ + if (!alert_data) { + return; + } + + const Component *subsystem = &sys_schema[alert_data->subsystem]; + const Component *component = &subsystem->components[alert_data->componentId]; + const Driver *driver = component->components[alert_data->deviceId].driver; + const Parameter *param = &driver->alerts[alert_id]; + + /* Count all previous parameters before this component */ + unsigned int param_sum = 0; + for (int i = 0; i < alert_data->deviceId; ++i) { + const Parameter *param = &component->components[i].driver->alerts[0]; + for (; _paramIsValid(param); ++param) { + param_sum += 1; + } + } + + uint16_t parameters = 0x01 << (param_sum + alert_id); + + /* Align to 4 byte boundary (bug in host) */ + size_t param_size = (_paramSize(param) + 3) & ~0x03; + + OCMPMessageFrame *pMsg = create_ocmp_msg_frame(alert_data->subsystem, + OCMP_MSG_TYPE_ALERT, + OCMP_AXN_TYPE_ACTIVE, + alert_data->componentId + 1, /* TODO: inconsistency indexing in host */ + parameters, + param_size); + if (pMsg) { + memcpy(pMsg->message.ocmp_data, data, _paramSize(param)); + Util_enqueueMsg(bigBrotherTxMsgQueue, semBigBrotherMsg, + (uint8_t*) pMsg); + } else { + LOGGER_ERROR("ERROR::Unable to allocate alert packet\n"); + } +} + +/***************************************************************************** + ** FUNCTION NAME : _handleMsgTypeCmd + ** + ** DESCRIPTION : handle all messages with command msgtype + ** + ** ARGUMENTS : pointer to message, pointer to compoenet + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _handleMsgTypeCmd(OCMPMessageFrame *pMsg, const Component *comp) +{ + const Command *cmd; + Component *dev; + if (comp) { + if (pMsg->message.parameters > 0) { + dev = &comp->components[(pMsg->message.parameters)-1]; + } else { + dev = comp; + } + if (dev->driver && dev->driver->commands) { + cmd = &dev->driver->commands[pMsg->message.action]; + } else { + cmd = &dev->commands[pMsg->message.action]; + } + if (cmd && cmd->cb_cmd) { + cmd->cb_cmd(dev->driver_cfg, pMsg->message.ocmp_data); + return true; + } + } + return false; +} + +/***************************************************************************** + ** FUNCTION NAME : OCMP_mallocFrame + ** + ** DESCRIPTION : handle message of config msgtype with set/get + ** actiontype + ** + ** ARGUMENTS : pointer to message , pointer to component, pointer to + ** parameter id , double pointer to the message payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _handle_cmd_get(OCMPMessageFrame *pMsg, const Component *comp, + unsigned int param_id, void *buf_ptr) +{ + switch (pMsg->message.msgtype) { + case OCMP_MSG_TYPE_CONFIG: + return (comp->driver->fxnTable->cb_get_config && + comp->driver->fxnTable->cb_get_config(comp->driver_cfg, param_id, + buf_ptr)); + case OCMP_MSG_TYPE_STATUS: + return (comp->driver->fxnTable->cb_get_status && + comp->driver->fxnTable->cb_get_status(comp->driver_cfg, param_id, + buf_ptr)); + default: + return false; + } +} + +/***************************************************************************** + ** FUNCTION NAME : _handle_cmd_set + ** + ** DESCRIPTION : handle message of config msgtype with set actiontype + ** + ** ARGUMENTS : pointer to message , pointer to component , pointer to + ** parameter id , pointer to the message payload + ** + ** RETURN TYPE : OCMPMessageFrame + ** + *****************************************************************************/ +static bool _handle_cmd_set(OCMPMessageFrame *pMsg, const Component *comp, + unsigned int param_id, const void *data) +{ + switch (pMsg->message.msgtype) { + case OCMP_MSG_TYPE_CONFIG: + return (comp->driver->fxnTable->cb_set_config && + comp->driver->fxnTable->cb_set_config(comp->driver_cfg, param_id, + data)); + default: + return false; + } +} + +/***************************************************************************** + ** FUNCTION NAME : _handleDevStatCfg + ** + ** DESCRIPTION : handle status and config msgtype messages + ** + ** ARGUMENTS : pointer to message , pointer to device, pointer to + ** parameter id , double pointer to the message payload + ** + ** RETURN TYPE : OCMPMessageFrame + ** + *****************************************************************************/ +static bool _handleDevStatCfg(OCMPMessageFrame *pMsg, const Component *dev, + unsigned int *param_id, uint8_t **buf_ptr) +{ + if (!dev->driver) { + return false; + } + + const Parameter *param_list = NULL; + switch (pMsg->message.msgtype) { + case OCMP_MSG_TYPE_CONFIG: + param_list = dev->driver->config; + break; + case OCMP_MSG_TYPE_STATUS: + param_list = dev->driver->status; + break; + default: + return false; + } + + if (!param_list) { + return false; + } + + bool dev_handled = false; + unsigned int normalized_id = 0; + while (param_list[normalized_id].name) { + if (pMsg->message.parameters & (1 << *param_id)) { + switch (pMsg->message.action) { + case OCMP_ACTION_TYPE_GET: + if (_handle_cmd_get(pMsg, dev, normalized_id, + *buf_ptr)) { + dev_handled = true; + } else { + pMsg->message.parameters &= ~(1 << *param_id); + } + break; + case OCMP_ACTION_TYPE_SET: + if (_handle_cmd_set(pMsg, dev, normalized_id, + *buf_ptr)) { + dev_handled = true; + } else { + pMsg->message.parameters &= ~(1 << *param_id); + } + break; + default: + pMsg->message.parameters &= ~(1 << *param_id); + break; + } + } + if (!dev->driver->payload_fmt_union) { + *buf_ptr += _paramSize(¶m_list[normalized_id]); + } + (*param_id)++; + normalized_id++; + } + return dev_handled; +} + +/***************************************************************************** + ** FUNCTION NAME : _handle_post_enable + ** + ** DESCRIPTION : handle post messages with enable action type + ** + ** ARGUMENTS : pointer to message, subsystem id + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _handle_post_enable(const Component *comp, OCMPMessageFrame *pMsg) +{ + bool ret = false; + OCMPMessageFrame *buffer; + const Post *postCmd = &comp->driver->post[(pMsg->message.action)-1]; + if (postCmd && postCmd->cb_postCmd) { + ret = postCmd->cb_postCmd(&buffer); + if (ret) { + Util_enqueueMsg(postRxMsgQueue, semPOSTMsg, (uint8_t*)buffer); + } + } + pMsg->message.ocmp_data[0] = !(ret); //RETURN_OK =0; + return ret; +} + +/***************************************************************************** + ** FUNCTION NAME : _handle_post_active + ** + ** DESCRIPTION : handle post message with active action type + ** + ** ARGUMENTS : pointer to message, subsystem id + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _handle_post_active(OCMPMessageFrame *pMsg,unsigned int subsystem_id) +{ + ReturnStatus status = _execPost(pMsg, subsystem_id); + return (status == RETURN_OK); +} + +/***************************************************************************** + ** FUNCTION NAME : _handle_post_get_results + ** + ** DESCRIPTION : handles messages with get result actiontype for post + ** + ** ARGUMENTS : pointer to component , pointer to message + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _handle_post_get_results(const Component *comp,OCMPMessageFrame *pMsg) +{ + bool ret = false; + const Post *postCmd = &comp->driver->post[(pMsg->message.action)-1]; + if (postCmd && postCmd->cb_postCmd) { + postCmd->cb_postCmd(pMsg); + ret = true; + } + return ret; +} + +/***************************************************************************** + ** FUNCTION NAME : _handleMsgTypePOST + ** + ** DESCRIPTION : handles all messages with post msgtype + ** + ** ARGUMENTS : pointer to message, pointer to comp, subsytem id + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _handleMsgTypePOST(OCMPMessageFrame *pMsg, const Component *comp, unsigned int subsystem_id) +{ + /* Determine driver & parameter */ + unsigned int param_id = 0; + uint8_t *buf_ptr = pMsg->message.ocmp_data; + bool dev_handled = false; + switch (pMsg->message.action) { + case OCMP_ACTION_TYPE_SET: + if (_handle_post_enable(comp, pMsg)) { + dev_handled = true; + } + break; + case OCMP_ACTION_TYPE_ACTIVE: + if (_handle_post_active(pMsg,subsystem_id)) { + dev_handled = true; + } + break; + case OCMP_ACTION_TYPE_GET: + if (_handle_post_get_results(comp, pMsg)) { + dev_handled = true; + } + break; +/* case OCMP_ACTION_REPLY: + if (_handle_post_reply(pMsg, *buf_ptr)) { + dev_handled = true; + } + break;*/ + default: + break; + } + return dev_handled; +} + +/***************************************************************************** + ** FUNCTION NAME : _handleMsgTypeStatCfg + ** + ** DESCRIPTION : handles status and config msg type + ** + ** ARGUMENTS : pointer to the msg and pointer to the comp + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _handleMsgTypeStatCfg(OCMPMessageFrame *pMsg, const Component *comp) +{ + /* Determine driver & parameter */ + unsigned int param_id = 0; + uint8_t *buf_ptr = pMsg->message.ocmp_data; + bool dev_handled = false; + + /* Handle component-level driver */ + if (_handleDevStatCfg(pMsg, comp, ¶m_id, &buf_ptr)) { + dev_handled = true; + } + + /* Handle sub-components (devices) */ + const Component *dev = &comp->components[0]; + for (; _compIsValid(dev); ++dev) { + if (_handleDevStatCfg(pMsg, dev, ¶m_id, &buf_ptr)) { + dev_handled = true; + } + } + return dev_handled; +} + +/***************************************************************************** + ** FUNCTION NAME : ocmp_route + ** + ** DESCRIPTION : Routing function which calls the required ocmp layer + ** + ** ARGUMENTS : pointer to the message frame, subsytem id + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool ocmp_route(OCMPMessageFrame *pMsg, unsigned int subsystem_id) +{ + const Component *subsystem = &sys_schema[subsystem_id]; + /* Validate component ID */ + if (pMsg->message.componentID > _subcompCount(subsystem)) { + LOGGER_ERROR("Component %d out of bounds\n", pMsg->message.componentID); + return false; + } + const Component *comp = &subsystem->components[(pMsg->message.componentID)-1]; + /* TODO: clean up special handling for commands */ + bool dev_handled = false; + switch (pMsg->message.msgtype) { + case OCMP_MSG_TYPE_COMMAND: + dev_handled = _handleMsgTypeCmd(pMsg, comp); + break; + case OCMP_MSG_TYPE_CONFIG: + case OCMP_MSG_TYPE_STATUS: + dev_handled = _handleMsgTypeStatCfg(pMsg, comp); + break; + case OCMP_MSG_TYPE_POST: + dev_handled = _handleMsgTypePOST(pMsg, comp, subsystem_id); + //pMsg->message.action = OCMP_ACTION_TYPE_REPLY; + //Util_enqueueMsg(postRxMsgQueue, semPOSTMsg, (uint8_t*) pMsg); + break; + default: + break; + } + + /* If we couldn't handle this message, return error */ + if (!dev_handled) { + pMsg->message.parameters = 0x00; + } + /* The main exception to the flow right now is POST - check for it first */ + if ((pMsg->message.msgtype == OCMP_MSG_TYPE_POST) && (pMsg->message.action == OCMP_ACTION_TYPE_ACTIVE)) { + pMsg->message.action = OCMP_ACTION_TYPE_REPLY; + Util_enqueueMsg(postRxMsgQueue, semPOSTMsg, (uint8_t*) pMsg); + } else if ((pMsg->message.msgtype == OCMP_MSG_TYPE_POST) && + (pMsg->message.action == OCMP_ACTION_TYPE_UPDATE)) { + Util_enqueueMsg(bigBrotherTxMsgQueue, semBigBrotherMsg, (uint8_t *)pMsg); + } else { + /* Send reply to the middleware */ + pMsg->message.action = OCMP_ACTION_TYPE_REPLY; + Util_enqueueMsg(bigBrotherTxMsgQueue, semBigBrotherMsg, (uint8_t *)pMsg); + } + return true; +} + +/***************************************************************************** + ** FUNCTION NAME : _subsystem_event_loop + ** + ** DESCRIPTION : subsytem module which is waits for message triger + ** from SSRegistry task + ** + ** ARGUMENTS : OCSubsystem, subsytem id + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void _subsystem_event_loop(UArg a0, UArg a1) +{ + OCSubsystem *ss = (OCSubsystem *)a0; + if (!ss) { + return; + } + + while (1) { + if (Semaphore_pend(ss->sem, BIOS_WAIT_FOREVER)) { + while (!Queue_empty(ss->msgQueue)) { + OCMPMessageFrame *pMsg = + (OCMPMessageFrame *) Util_dequeueMsg(ss->msgQueue); + + if (pMsg) { + /* Attempt to route the message to the correct driver + (if successful, no need to clean up message here) */ + if (!ocmp_route(pMsg, a1)) { + LOGGER_ERROR("ERROR:: Unable to route OCMP message\n"); + free(pMsg); + } + } + } + } + } +} + +/***************************************************************************** + ** FUNCTION NAME : subsystem_init + ** + ** DESCRIPTION : API to initialise ss_reg and create subsytem tasks + ** + ** ARGUMENTS : subsytem id + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void subsystem_init(OCMPSubsystem ss_id) { + OCSubsystem *ss = (OCSubsystem*)malloc(sizeof(OCSubsystem)); + if (!ss) { + return; + } + ss_reg[ss_id] = ss; + ss->state = SS_STATE_PWRON; + + /* Create Semaphore for RX Message Queue */ + Semaphore_construct(&ss->semStruct, 0, NULL); + ss->sem = Semaphore_handle(&ss->semStruct); + if (!ss->sem) { + LOGGER_DEBUG("SS REG:ERROR:: Failed in Creating RX Semaphore for " + "subsystem %d\n", ss_id); + } + + /* Create Message Queue for RX Messages */ + ss->msgQueue = Util_constructQueue(&ss->queueStruct); + if (!ss->msgQueue) { + LOGGER_ERROR("SS REG:ERROR:: Failed in Constructing Message Queue for " + "RX Message for subsystem %d\n", ss_id); + } + + /* Spin up the task */ + Task_Params taskParams; + Task_Params_init(&taskParams); + taskParams.stack = OC_task_stack[ss_id];// ss->taskStack; + taskParams.stackSize = OC_TASK_STACK_SIZE;//ss->taskStackSize; + taskParams.priority = OC_TASK_PRIORITY;//ss->taskPriority; + taskParams.arg0 = (UArg)ss; + taskParams.arg1 = ss_id; + + Task_construct(&ss->taskStruct, _subsystem_event_loop, &taskParams, NULL); + LOGGER_DEBUG("SS REG:DEBUG:: Creating Task for Subsystem %d\n", ss_id); +} + +/***************************************************************************** + ** FUNCTION NAME : SSRegistry_init + ** + ** DESCRIPTION : initialise the ss_reg sturcture during system init + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void SSRegistry_init(void) { + for (OCMPSubsystem i = (OCMPSubsystem)0; i < SUBSYSTEM_COUNT; ++i) { + subsystem_init(i); + } +} + +/***************************************************************************** + ** FUNCTION NAME : SSRegistry_Get + ** + ** DESCRIPTION : API to return the ss registry entry given the + ** subsytem id + ** + ** ARGUMENTS : subsystem id + ** + ** RETURN TYPE : OCSubsystem* + ** + *****************************************************************************/ +OCSubsystem* SSRegistry_Get(OCMPSubsystem ss_id) { + if (ss_id >= SUBSYSTEM_COUNT) { + return NULL; + } + return ss_reg[ss_id]; +} + +/***************************************************************************** + ** FUNCTION NAME : SSRegistry_sendMessage + ** + ** DESCRIPTION : API to send message to the desired susbsytem + ** + ** ARGUMENTS : subsytem id , pointer to the msg to be sent + ** + ** RETURN TYPE : TRUE or FALSE + ** + *****************************************************************************/ +bool SSRegistry_sendMessage(OCMPSubsystem ss_id, void *pMsg) { + OCSubsystem *ss = SSRegistry_Get(ss_id); + if (!ss) { + return false; + } + + return Util_enqueueMsg(ss->msgQueue, ss->sem, (uint8_t*) pMsg); +} diff --git a/firmware/psu/src/registry/SSRegistry.h b/firmware/psu/src/registry/SSRegistry.h new file mode 100644 index 0000000000..e19d345073 --- /dev/null +++ b/firmware/psu/src/registry/SSRegistry.h @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#ifndef _SSREGISTRY_H_ +#define _SSREGISTRY_H_ + +#include "common/inc/global/ocmp_frame.h" +#include "inc/common/system_states.h" + +#include +#include +#include + +#include + +typedef void (*SS_ProcessMsg_Cb)(OCMPMessageFrame *pBmsMsg); +/** + * Common subsystem attributes (message queue, task entry point, etc.) + */ +typedef struct OCSubsystem { + /* Message queue handles */ + Queue_Handle msgQueue; + Semaphore_Handle sem; + + /* Private variables (reduce dynamic allocation needs) */ + Queue_Struct queueStruct; + Semaphore_Struct semStruct; + Task_Struct taskStruct; + eSubSystemStates state; +} OCSubsystem; + +/** + * Initializes the subsystem registry by creating message queues + * and spawning the tasks for each subsystem + */ +void SSRegistry_init(void); + +/** + * Retrieves the pointer to the #OCSubsystem struct associated with a given ID + * @param ss_id The ID of the requested subsystem + * @return #OCSubsystem pointer if valid ID, else NULL + */ +OCSubsystem* SSRegistry_Get(OCMPSubsystem ss_id); + +/** + * Enters a message into the desired subsystem's message queue + * @note This is mostly for legacy support for the alerts manager + * @param ss_id ID of the subsystem to send the message to + * @param pMsg Message pointer to be inserted into the subsystem's queue + * @return true if successful, false if subsystem not found or queue full + */ +bool SSRegistry_sendMessage(OCMPSubsystem ss_id, void *pMsg); + +#endif /* _SSREGISTRY_H_ */ diff --git a/firmware/psu/src/subsystem/power/power.c b/firmware/psu/src/subsystem/power/power.c new file mode 100644 index 0000000000..07d969d9ce --- /dev/null +++ b/firmware/psu/src/subsystem/power/power.c @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +//***************************************************************************** +// HEADER FILES +/* Board Header files */ +#include "common/inc/global/Framework.h" +#include "inc/subsystem/power/power.h" +#include "inc/common/post.h" + +#define SUBSYTEM_CHECK (PostResult[iter].subsystem <= getpostResultMsg->message.ocmp_data[0]) +extern POSTData PostResult[POST_RECORDS]; + +/***************************************************************************** + ** FUNCTION NAME : psuCore_pre_init + ** + ** DESCRIPTION : Get POST results from EEPROM. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : bool + ** + *****************************************************************************/ +bool psuCore_pre_init(void *driver, void *returnValue) +{ + //Configuring GPIOS + PWRSRC_Dev *gpioCfg = (PWRSRC_Dev *)driver; + + OcGpio_configure(&gpioCfg->cfg.pin_dc_present, OCGPIO_CFG_INPUT); + OcGpio_configure(&gpioCfg->cfg.pin_poe_prsnt_n, OCGPIO_CFG_INPUT); + OcGpio_configure(&gpioCfg->cfg.pin_int_bat_prsnt, OCGPIO_CFG_INPUT); + OcGpio_configure(&gpioCfg->cfg.pin_disable_dc_input, OCGPIO_CFG_OUTPUT); + OcGpio_configure(&gpioCfg->cfg.pin_dc_input_fault, OCGPIO_CFG_INPUT); + OcGpio_configure(&gpioCfg->cfg.pin_oc_input_present, OCGPIO_CFG_INPUT); + OcGpio_configure(&gpioCfg->cfg.pin_power_off, OCGPIO_CFG_OUTPUT); + OcGpio_write(&gpioCfg->cfg.pin_disable_dc_input, false); + OcGpio_write(&gpioCfg->cfg.pin_power_off, false); + + return true; +} + +/***************************************************************************** + ** FUNCTION NAME : PWR_post_get_results + ** + ** DESCRIPTION : Get POST results from EEPROM. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : bool + ** + *****************************************************************************/ +bool PWR_post_get_results(void **getpostResult) +{ + ReturnStatus status = RETURN_OK; + /* Return the POST results*/ + uint8_t iter = 0x00; + uint8_t index = 0x00; + OCMPMessageFrame *getpostResultMsg = (OCMPMessageFrame *)getpostResult; + /* Get the subsystem info for which message is required */ + OCMPMessageFrame *postResultMsg = create_ocmp_msg_frame( + getpostResultMsg->message.subsystem, OCMP_MSG_TYPE_POST, + 0x05,0x00,0x00,40); + if (postResultMsg) { + /* Getting data assigned*/ + postResultMsg->header.ocmpSof = getpostResultMsg->header.ocmpSof; + postResultMsg->header.ocmpInterface = getpostResultMsg->header + .ocmpInterface; + postResultMsg->header.ocmpSeqNumber = getpostResultMsg->header + .ocmpSeqNumber; + for (iter = 0; SUBSYTEM_CHECK; iter++) { + if (PostResult[iter].subsystem + == getpostResultMsg->message.ocmp_data[0]) { + postResultMsg->message.ocmp_data[(3 * index) + 0] = + PostResult[iter].subsystem; + postResultMsg->message.ocmp_data[(3 * index) + 1] = + PostResult[iter].devSno; //Device serial Number + postResultMsg->message.ocmp_data[(3 * index) + 2] = + PostResult[iter].status; //Status ok + index++; + } + } + LOGGER_DEBUG("POWER:INFO::POST message sent for subsystem 0x%x.\n"); + /*Size of payload*/ + postResultMsg->header.ocmpFrameLen = index * 3; + /*Updating Subsystem*/ + //postResultMsg->message.subsystem = (OCMPSubsystem)PostResult[iter].subsystem; + /* Number of devices found under subsystem*/ + postResultMsg->message.parameters = index; + index = 0; + } else { + LOGGER("POWER:ERROR:: Failed to allocate memory for POST results.\n"); + } + memcpy(((OCMPMessageFrame*)getpostResult), postResultMsg, 64); + return status; +} diff --git a/firmware/psu/src/utils/ocmp_util.c b/firmware/psu/src/utils/ocmp_util.c new file mode 100644 index 0000000000..5edc4c66f4 --- /dev/null +++ b/firmware/psu/src/utils/ocmp_util.c @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include "inc/utils/ocmp_util.h" + +/***************************************************************************** + ** FUNCTION NAME : OCMP_mallocFrame + ** + ** DESCRIPTION : API to allocate an OCMP frame of a given data length + ** + ** ARGUMENTS : size of the payload of frame + ** + ** RETURN TYPE : OCMPMessageFrame + ** + *****************************************************************************/ +OCMPMessageFrame * OCMP_mallocFrame(uint16_t len) +{ + OCMPMessageFrame *pMsg; + // Allocate memory for NPI Frame + pMsg = (OCMPMessageFrame *)malloc(sizeof(OCMPMessageFrame)+len); + if (pMsg != NULL) { + // Assign Data Length of Frame + pMsg->header.ocmpFrameLen = len; + // Assign pData to first byte of payload + // Pointer arithmetic of + 1 is equal to sizeof(OCMPMessageFrame) bytes + // then cast to unsigned char * for pData + //pMsg->message.ocmp_data = (unsigned char *)(pMsg + 1); + //pMsg->message.ocmp_data = (unsigned char *)(pMsg + 2); + } + return pMsg; +} + +/***************************************************************************** + ** FUNCTION NAME : create_ocmp_msg_frame + ** + ** DESCRIPTION : Create a OCMP message. + ** + ** ARGUMENTS : OCMPSubsystem subSystem, + ** OCMPMsgType msgtype, + ** OCMPActionType actionType, + ** ComponentId, + ** ParemeterID, + ** Payload size + ** + ** RETURN TYPE : OCMPMessageFrame + ** + *****************************************************************************/ +OCMPMessageFrame* create_ocmp_msg_frame(OCMPSubsystem subSystem, + OCMPMsgType msgtype, + OCMPActionType actionType, + uint8_t componentId, + uint16_t parameters, + uint8_t payloadSize) +{ + OCMPMessageFrame *ocmp_msg = (OCMPMessageFrame *) OCMP_mallocFrame( + payloadSize); + if (ocmp_msg) { + *ocmp_msg = (OCMPMessageFrame){ + .header = { + .ocmpSof = OCMP_MSG_SOF, + .ocmpInterface = OCMP_COMM_IFACE_UART, + .ocmpFrameLen = payloadSize, + //.ocmp_seqNumber = 0x00; + //.ocmp_timestamp = 0x00; //Get RTC TimeStamp + }, + .message = { + .subsystem = subSystem, + .componentID = componentId, + .parameters = parameters, + .msgtype = msgtype, + .action = actionType, + } + }; + memset(&(ocmp_msg->message.ocmp_data[0]),0x00,payloadSize); + } + return ocmp_msg; +} + +/***************************************************************************** + ** FUNCTION NAME : create_ocmp_alert_from_Evt + ** + ** DESCRIPTION : Create the OCMP Alert frame from the Event message. + ** + ** ARGUMENTS : OCMPMessageFrame to be used to create Alert, + ** ComponentId, + ** ParemeterID + ** + ** RETURN TYPE : OCMPMessageFrame + ** + *****************************************************************************/ +OCMPMessageFrame* create_ocmp_alert_from_Evt(OCMPMessageFrame* ocmpEventMsg, + uint8_t componentId, + uint16_t parameters ) +{ + OCMPMessageFrame *ocmpAlertMsg = + (OCMPMessageFrame *) OCMP_mallocFrame(1); + if (ocmpAlertMsg != NULL) { + memset(ocmpAlertMsg, 0x00, (sizeof(OCMPMessageFrame))); + memcpy(ocmpAlertMsg, ocmpEventMsg, + (sizeof(OCMPMessageFrame)) + 1); + ocmpAlertMsg->message.msgtype = OCMP_MSG_TYPE_ALERT; + ocmpAlertMsg->message.componentID = componentId; + ocmpAlertMsg->message.parameters = parameters; + } + return ocmpAlertMsg; +} diff --git a/firmware/psu/src/utils/util.c b/firmware/psu/src/utils/util.c new file mode 100644 index 0000000000..e94482af90 --- /dev/null +++ b/firmware/psu/src/utils/util.c @@ -0,0 +1,329 @@ +/******************************************************************************* + Filename: util.c + Revised: $Date: 2015-06-02 11:18:40 -0700 (Tue, 02 Jun 2015) $ + Revision: $Revision: 43957 $ + + Description: This file contains utility functions. + + Copyright 2014 Texas Instruments Incorporated. All rights reserved. + + IMPORTANT: Your use of this Software is limited to those specific rights + granted under the terms of a software license agreement between the user + who downloaded the software, his/her employer (which must be your employer) + and Texas Instruments Incorporated (the "License"). You may not use this + Software unless you agree to abide by the terms of the License. The License + limits your use, and you acknowledge, that the Software may not be modified, + copied or distributed unless embedded on a Texas Instruments microcontroller + or used solely and exclusively in conjunction with a Texas Instruments radio + frequency transceiver, which is integrated into your product. Other than for + the foregoing purpose, you may not use, reproduce, copy, prepare derivative + works of, modify, distribute, perform, display or sell this Software and/or + its documentation for any purpose. + + YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE + PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, + INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE, + NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL + TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT, + NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER + LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES + INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE + OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT + OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES + (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS. + + Should you have any questions regarding your right to use this Software, + contact Texas Instruments Incorporated at www.TI.com. +*******************************************************************************/ + +/********************************************************************* + * INCLUDES + */ +#include "inc/utils/util.h" + +#include +#include +#include + +#include +#include + +/********************************************************************* + * TYPEDEFS + */ + +// RTOS queue for profile/app messages. +typedef struct _queueRec_ +{ + Queue_Elem _elem; // queue element + uint8_t *pData; // pointer to app data +} queueRec_t; + +/********************************************************************* + * LOCAL FUNCTIONS + */ + +/******************************************************************************* + * EXTERNAL VARIABLES + */ + +/********************************************************************* + * LOCAL VARIABLES + */ + +/********************************************************************* + * PUBLIC FUNCTIONS + */ + +/********************************************************************* + * @fn Util_constructClock + * + * @brief Initialize a TIRTOS Clock instance. + * + * @param pClock - pointer to clock instance structure. + * @param clockCB - callback function upon clock expiration. + * @param clockDuration - longevity of clock timer in milliseconds + * @param clockPeriod - if set to a value other than 0, the first + * expiry is determined by clockDuration. All + * subsequent expiries use the clockPeriod value. + * @param startFlag - TRUE to start immediately, FALSE to wait. + * @param arg - argument passed to callback function. + * + * @return Clock_Handle - a handle to the clock instance. + */ +Clock_Handle Util_constructClock(Clock_Struct *pClock, + Clock_FuncPtr clockCB, + uint32_t clockDuration, + uint32_t clockPeriod, + uint8_t startFlag, + UArg arg) +{ + Clock_Params clockParams; + + // Convert clockDuration in milliseconds to ticks. + uint32_t clockTicks = clockDuration * (1000 / Clock_tickPeriod); + + // Setup parameters. + Clock_Params_init(&clockParams); + + // Setup argument. + clockParams.arg = arg; + + // If period is 0, this is a one-shot timer. + clockParams.period = clockPeriod * (1000 / Clock_tickPeriod); + + // Starts immediately after construction if true, otherwise wait for a call + // to start. + clockParams.startFlag = startFlag; + + // Initialize clock instance. + Clock_construct(pClock, clockCB, clockTicks, &clockParams); + + return Clock_handle(pClock); +} + +/********************************************************************* + * @fn Util_startClock + * + * @brief Start a clock. + * + * @param pClock - pointer to clock struct + * + * @return none + */ +void Util_startClock(Clock_Struct *pClock) +{ + Clock_Handle handle = Clock_handle(pClock); + + // Start clock instance + Clock_start(handle); +} + +/********************************************************************* + * @fn Util_restartClock + * + * @brief Restart a clock by changing the timeout. + * + * @param pClock - pointer to clock struct + * @param clockTimeout - longevity of clock timer in milliseconds + * + * @return none + */ +void Util_restartClock(Clock_Struct *pClock, uint32_t clockTimeout) +{ + uint32_t clockTicks; + Clock_Handle handle; + + handle = Clock_handle(pClock); + + if (Clock_isActive(handle)) + { + // Stop clock first + Clock_stop(handle); + } + + // Convert timeout in milliseconds to ticks. + clockTicks = clockTimeout * (1000 / Clock_tickPeriod); + + // Set the initial timeout + Clock_setTimeout(handle, clockTicks); + + // Start clock instance + Clock_start(handle); +} + +/********************************************************************* + * @fn Util_isActive + * + * @brief Determine if a clock is currently active. + * + * @param pClock - pointer to clock struct + * + * @return TRUE or FALSE + */ +bool Util_isActive(Clock_Struct *pClock) +{ + Clock_Handle handle = Clock_handle(pClock); + + // Start clock instance + return Clock_isActive(handle); +} + +/********************************************************************* + * @fn Util_stopClock + * + * @brief Stop a clock. + * + * @param pClock - pointer to clock struct + * + * @return none + */ +void Util_stopClock(Clock_Struct *pClock) +{ + Clock_Handle handle = Clock_handle(pClock); + + // Stop clock instance + Clock_stop(handle); +} + +/********************************************************************* + * @fn Util_rescheduleClock + * + * @brief Reschedule a clock by changing the timeout and period values. + * + * @param pClock - pointer to clock struct + * @param clockPeriod - longevity of clock timer in milliseconds + * @return none + */ +void Util_rescheduleClock(Clock_Struct *pClock, uint32_t clockPeriod) +{ + bool running; + uint32_t clockTicks; + Clock_Handle handle; + + handle = Clock_handle(pClock); + running = Clock_isActive(handle); + + if (running) + { + Clock_stop(handle); + } + + // Convert period in milliseconds to ticks. + clockTicks = clockPeriod * (1000 / Clock_tickPeriod); + + Clock_setTimeout(handle, clockTicks); + Clock_setPeriod(handle, clockTicks); + + if (running) + { + Clock_start(handle); + } +} + +/********************************************************************* + * @fn Util_constructQueue + * + * @brief Initialize an RTOS queue to hold messages to be processed. + * + * @param pQueue - pointer to queue instance structure. + * + * @return A queue handle. + */ +Queue_Handle Util_constructQueue(Queue_Struct *pQueue) +{ + // Construct a Queue instance. + Queue_construct(pQueue, NULL); + + return Queue_handle(pQueue); +} + +/********************************************************************* + * @fn Util_enqueueMsg + * + * @brief Creates a queue node and puts the node in RTOS queue. + * + * @param msgQueue - queue handle. + * @param sem - thread's event processing semaphore that queue is + * associated with. + * @param pMsg - pointer to message to be queued + * + * @return TRUE if message was queued, FALSE otherwise. + */ +uint8_t Util_enqueueMsg(Queue_Handle msgQueue, Semaphore_Handle sem, + uint8_t *pMsg) +{ + queueRec_t *pRec; + + // Allocated space for queue node. + + if (pRec = (queueRec_t *)malloc(sizeof(queueRec_t))) + { + pRec->pData = pMsg; + + Queue_enqueue(msgQueue, &pRec->_elem); + + // Wake up the application thread event handler. + if (sem) + { + Semaphore_post(sem); + } + + return TRUE; + } + + // Free the message. + + free(pMsg); + + + return FALSE; +} + +/********************************************************************* + * @fn Util_dequeueMsg + * + * @brief Dequeues the message from the RTOS queue. + * + * @param msgQueue - queue handle. + * + * @return pointer to dequeued message, NULL otherwise. + */ +uint8_t *Util_dequeueMsg(Queue_Handle msgQueue) +{ + if (!Queue_empty(msgQueue)) + { + queueRec_t *pRec = Queue_dequeue(msgQueue); + uint8_t *pData = pRec->pData; + + // Free the queue node + // Note: this does not free space allocated by data within the node. + free(pRec); + + + return pData; + } + + return NULL; +} + diff --git a/firmware/psu/tm4c1230e6pm.cmd b/firmware/psu/tm4c1230e6pm.cmd new file mode 100644 index 0000000000..68250a18fa --- /dev/null +++ b/firmware/psu/tm4c1230e6pm.cmd @@ -0,0 +1,45 @@ +/****************************************************************************** + * + * Default Linker Command file for the Texas Instruments TM4C1230E6PM + * + * This is derived from revision 15071 of the TivaWare Library. + * + *****************************************************************************/ + +--retain=g_pfnVectors + +MEMORY +{ + FLASH (RX) : origin = 0x00000000, length = 0x00020000 + SRAM (RWX) : origin = 0x20000000, length = 0x00008000 +} + +/* The following command line options are set as part of the CCS project. */ +/* If you are building using the command line, or for some reason want to */ +/* define them here, you can uncomment and modify these lines as needed. */ +/* If you are using CCS for building, it is probably better to make any such */ +/* modifications in your CCS project and leave this file alone. */ +/* */ +/* --heap_size=0 */ +/* --stack_size=256 */ +/* --library=rtsv7M4_T_le_eabi.lib */ + +/* Section allocation in memory */ + +SECTIONS +{ + .intvecs: > 0x00000000 + .text : > FLASH + .const : > FLASH + .cinit : > FLASH + .pinit : > FLASH + .init_array : > FLASH + + .vtable : > 0x20000000 + .data : > SRAM + .bss : > SRAM + .sysmem : > SRAM + .stack : > SRAM +} + +__STACK_TOP = __stack + 512; diff --git a/firmware/src/.exclude b/firmware/src/.exclude new file mode 100644 index 0000000000..8c863310d8 --- /dev/null +++ b/firmware/src/.exclude @@ -0,0 +1 @@ +This file exists to prevent Eclipse/CDT from adding the C sources contained in this directory (or below) to any enclosing project. diff --git a/firmware/src/Board.h b/firmware/src/Board.h new file mode 100644 index 0000000000..7a8842ee49 --- /dev/null +++ b/firmware/src/Board.h @@ -0,0 +1,92 @@ +/******************************************************************************* + Filename: Board.h + Revised: $Date: 2015-06-02 11:18:40 -0700 (Tue, 02 Jun 2015) $ + Revision: $Revision: 43957 $ + + Description: This file contains utility functions. + + Copyright 2014 Texas Instruments Incorporated. All rights reserved. + + IMPORTANT: Your use of this Software is limited to those specific rights + granted under the terms of a software license agreement between the user + who downloaded the software, his/her employer (which must be your employer) + and Texas Instruments Incorporated (the "License"). You may not use this + Software unless you agree to abide by the terms of the License. The License + limits your use, and you acknowledge, that the Software may not be modified, + copied or distributed unless embedded on a Texas Instruments microcontroller + or used solely and exclusively in conjunction with a Texas Instruments radio + frequency transceiver, which is integrated into your product. Other than for + the foregoing purpose, you may not use, reproduce, copy, prepare derivative + works of, modify, distribute, perform, display or sell this Software and/or + its documentation for any purpose. + + YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE + PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, + INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE, + NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL + TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT, + NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER + LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES + INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE + OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT + OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES + (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS. + + Should you have any questions regarding your right to use this Software, + contact Texas Instruments Incorporated at www.TI.com. +*******************************************************************************/ +#ifndef __BOARD_H +#define __BOARD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "common/inc/global/OC_CONNECT1.h" + +#define Board_initEMAC OC_CONNECT1_initEMAC +#define Board_initGeneral OC_CONNECT1_initGeneral +#define Board_initGPIO OC_CONNECT1_initGPIO +#define Board_initI2C OC_CONNECT1_initI2C +#define Board_initUART OC_CONNECT1_initUART +#define Board_initUSB OC_CONNECT1_initUSB +#define Board_initWatchdog OC_CONNECT1_initWatchdog + +#define Board_IOEXP_ALERT OC_EC_GBC_IOEXP71_ALERT +#define Board_ECINA_ALERT OC_EC_GBC_INA_ALERT +#define Board_APINA_ALERT OC_EC_GBC_AP_INA_ALERT +#define Board_SDRFPGA_TEMPINA_ALERT OC_EC_SDR_FPGA_TEMP_INA_ALERT +#define Board_SDR_INA_ALERT OC_EC_SDR_INA_ALERT +#define Board_RFFE_TEMP_INA_ALERT OC_EC_RFFE_TEMP_INA_ALERT +#define Board_SYNC_IOEXP_ALERT OC_EC_SYNC_IOEXP_ALERT +#define Board_LeadAcidAlert OC_EC_PWR_LACID_ALERT +#define Board_LithiumIonAlert OC_EC_PWR_LION_ALERT +#define Board_PSEALERT OC_EC_GBC_PSE_ALERT +#define Board_PD_PWRGDAlert OC_EC_PD_PWRGD_ALERT +#define Board_SOC_UART3_TX OC_EC_SOC_UART3_TX + +#define Board_I2C0 OC_CONNECT1_I2C0 +#define Board_I2C1 OC_CONNECT1_I2C1 +#define Board_I2C2 OC_CONNECT1_I2C2 +#define Board_I2C3 OC_CONNECT1_I2C3 +#define Board_I2C4 OC_CONNECT1_I2C4 +#define Board_I2C6 OC_CONNECT1_I2C6 +#define Board_I2C7 OC_CONNECT1_I2C7 +#define Board_I2C8 OC_CONNECT1_I2C8 +#define Board_I2CCOUNT OC_CONNECT1_I2CCOUNT + +#define Board_USBHOST OC_CONNECT1_USBHOST +#define Board_USBDEVICE OC_CONNECT1_USBDEVICE + +// TODO: maybe rename to "UART_GSM" and stuff to be more abstracted from HW +#define Board_UART0 OC_CONNECT1_UART0 +#define Board_UART3 OC_CONNECT1_UART3 +#define Board_UART4 OC_CONNECT1_UART4 +#define Board_UARTXR0 OC_CONNECT1_UARTXR0 +#define Board_WATCHDOG0 OC_CONNECT1_WATCHDOG0 + +#ifdef __cplusplus +} +#endif + +#endif /* __BOARD_H */ diff --git a/firmware/src/bigbrother.c b/firmware/src/bigbrother.c new file mode 100644 index 0000000000..f073dbafaf --- /dev/null +++ b/firmware/src/bigbrother.c @@ -0,0 +1,369 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** +#include "inc/common/bigbrother.h" + +#include "Board.h" +#include "comm/gossiper.h" +#include "common/inc/global/ocmp_frame.h" +#include "drivers/OcGpio.h" +#include "inc/common/post.h" +#include "inc/common/system_states.h" +#include "inc/subsystem/hci/hci_buzzer.h" +#include "inc/utils/ocmp_util.h" +#include "registry/SSRegistry.h" + +#include + +#include +extern OcGpio_Port pwr_io; +extern OcGpio_Port ec_io; + +OcGpio_Pin pin_24v = { &pwr_io, 3}; +OcGpio_Pin pin_5v0 = { &pwr_io, 4}; +OcGpio_Pin pin_3v3 = { &pwr_io, 5}; +OcGpio_Pin pin_gbcv2_on = { &pwr_io, 6}; +OcGpio_Pin pin_12v_bb = { &pwr_io, 7}; +OcGpio_Pin pin_12v_fe = { &pwr_io, 8}; +OcGpio_Pin pin_20v_fe = { &pwr_io, 9}; +OcGpio_Pin pin_1v8 = { &pwr_io, 10}; + + +/* Global Task Configuration Variables */ +Task_Struct bigBrotherTask; +Char bigBrotherTaskStack[BIGBROTHER_TASK_STACK_SIZE]; + +eSubSystemStates oc_sys_state = SS_STATE_PWRON; +//***************************************************************************** +// HANDLES DEFINITION +//***************************************************************************** +/* Queue object */ +/* + * Semaphore for the Big Brother task where it will be waiting on. + * Sub systems or Gossiper post this with respective queues filled in. + */ +Semaphore_Handle semBigBrotherMsg; + +static Queue_Struct bigBrotherRxMsg; +static Queue_Struct bigBrotherTxMsg; + +/* + * bigBrotherRxMsgQueue - Used by the gossiper to pass the frame once recived + * from Ethernet or UART and processed and needs to forward to bigBrother. + */ +Queue_Handle bigBrotherRxMsgQueue; +/* + * bigBrotherTxMsgQueue - Used by the BigBrother to pass the frame once + * underlying subsystem processed it (GPP/RF etc) and need to send back + * to Gosipper. This is the one all the subsystem will be listening only. + */ +Queue_Handle bigBrotherTxMsgQueue; + +/***************************************************************************** + * FUNCTION DECLARATIONS + *****************************************************************************/ +static void bigbrother_taskfxn(UArg a0, UArg a1); +static void bigbrother_init(void); +static ReturnStatus bigbrother_process_rx_msg(uint8_t *pMsg); +static ReturnStatus bigbrother_process_tx_msg(uint8_t *pMsg); +extern void post_createtask(void); +static void bigborther_initiate_post(void); + +extern void gossiper_createtask(void); +extern void usb_rx_createtask(void); +extern void usb_tx_createtask(void); +extern void uartdma_rx_createtask(void); +extern void uartdma_tx_createtask(void); +//extern void watchdog_create_task(void);; + +/***************************************************************************** + ** FUNCTION NAME : bb_sys_post_complete + ** + ** DESCRIPTION : Get POST results from EEPROM. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : ReturnStatus + ** + *****************************************************************************/ +ReturnStatus bb_sys_post_complete() +{ + ReturnStatus status = RETURN_OK; + LOGGER_DEBUG("BIGBROTHER:INFO::POST test is completed.\n"); + static uint8_t count = 0; + + if (count == 0) { + // Task_sleep(60000); + // TODO: Starting UART DMA Interface based on EBMP. + uartdma_rx_createtask(); // P - 07 + uartdma_tx_createtask(); // P - 07 + count++; + // uart_enable(); + /* TODO: enable this back */ +#if ENABLE_POWER + OcGpio_configure(&pin_24v, + OCGPIO_CFG_OUTPUT | OCGPIO_CFG_OUT_LOW); + OcGpio_write(&pin_24v, 1); + + OcGpio_configure(&pin_5v0, + OCGPIO_CFG_OUTPUT | OCGPIO_CFG_OUT_LOW); + OcGpio_write(&pin_5v0, 1); + + OcGpio_configure(&pin_3v3, + OCGPIO_CFG_OUTPUT | OCGPIO_CFG_OUT_LOW); + OcGpio_write(&pin_3v3, 1); + + OcGpio_configure(&pin_gbcv2_on, + OCGPIO_CFG_OUTPUT | OCGPIO_CFG_OUT_LOW); + OcGpio_write(&pin_gbcv2_on, 1); + + OcGpio_configure(&pin_12v_bb, + OCGPIO_CFG_OUTPUT | OCGPIO_CFG_OUT_LOW); + OcGpio_write(&pin_12v_bb, 1); + + OcGpio_configure(&pin_12v_fe, + OCGPIO_CFG_OUTPUT | OCGPIO_CFG_OUT_LOW); + OcGpio_write(&pin_12v_fe, 1); + + OcGpio_configure(&pin_20v_fe, + OCGPIO_CFG_OUTPUT | OCGPIO_CFG_OUT_LOW); + OcGpio_write(&pin_20v_fe, 1); + + OcGpio_configure(&pin_1v8, + OCGPIO_CFG_OUTPUT | OCGPIO_CFG_OUT_LOW); + OcGpio_write(&pin_1v8, 1); +#endif + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : bigbrother_process_tx_msg + ** + ** DESCRIPTION : Processes the big brother outgoing messages. + ** + ** ARGUMENTS : Pointer to BIGBROTHER_TXEvt_t structure + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static ReturnStatus bigbrother_process_tx_msg(uint8_t *pMsg) +{ + ReturnStatus status = RETURN_OK; + LOGGER_DEBUG("BIGBROTHER:INFO:: Processing Big Brother TX Message.\n"); + if (pMsg != NULL) { + Util_enqueueMsg(gossiperTxMsgQueue, semGossiperMsg, (uint8_t*) pMsg); + } else { + LOGGER_ERROR("BIGBROTHER::ERROR::No Valid Pointer.\n"); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : bigbrother_process_rx_msg + ** + ** DESCRIPTION : Processes the big brother incoming messages. + ** + ** ARGUMENTS : Pointer to BIGBROTHER_RXEvt_t structure + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static ReturnStatus bigbrother_process_rx_msg(uint8_t *pMsg) +{ + ReturnStatus status = RETURN_OK; + LOGGER_DEBUG("BIGBROTHER:INFO:: Processing Big Brother RX Message.\n"); + OCMPMessageFrame * pOCMPMessageFrame = (OCMPMessageFrame *) pMsg; + if (pOCMPMessageFrame != NULL) { + LOGGER_DEBUG("BIGBROTHER:INFO:: RX Msg recieved with Length: 0x%x," + "Interface: 0x%x, Seq.No: 0x%x, TimeStamp: 0x%x.\n", + pOCMPMessageFrame->header.ocmpFrameLen, + pOCMPMessageFrame->header.ocmpInterface, + pOCMPMessageFrame->header.ocmpSeqNumber, + pOCMPMessageFrame->header.ocmpTimestamp); + // Forward this to respective subsystem. + if (!SSRegistry_sendMessage(pOCMPMessageFrame->message.subsystem, + pMsg)) { + LOGGER_ERROR("BIGBROTHER::ERROR::Subsystem %d doesn't exist\n", + pOCMPMessageFrame->message.subsystem); + free(pMsg); + } + } else { + LOGGER_ERROR("BIGBROTHER:ERROR:: No message recieved.\n"); + free(pMsg); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : bigborther_initiate_post + ** + ** DESCRIPTION : Creates POST test task. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void bigborther_initiate_post(void) +{ + LOGGER_DEBUG("BIGBROTHER:INFO::Creating task to perform POST.\n"); + post_createtask(); +} + +/***************************************************************************** + ** FUNCTION NAME : bigbrother_ioexp_init + ** + ** DESCRIPTION : Initializes Io expander SX1509. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +OcGpio_Pin pin_v5_a_pgood = { &ec_io, OC_EC_PGOOD_5V0}; +OcGpio_Pin pin_v12_a_pgood = { &ec_io, OC_EC_PGOOD_12V0}; + +ReturnStatus bigbrother_ioexp_init(void) +{ + ReturnStatus status = RETURN_OK; + + OcGpio_init(&pwr_io); + + /* Initialize pins that aren't covered yet by a subsystem */ + OcGpio_configure(&pin_v5_a_pgood, OCGPIO_CFG_INPUT); + OcGpio_configure(&pin_v12_a_pgood, OCGPIO_CFG_INPUT); + + return status; +} + +/******************************************************************************* + ** FUNCTION NAME : bigborther_spwan_task + ** + ** DESCRIPTION : Application task start up point for open cellular. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + ******************************************************************************/ +static void bigborther_spwan_task(void) +{ + /* Read OC UID EEPROM */ + + /* Check the list for possible devices connected. */ + + /* Launches other tasks */ +// usb_rx_createtask(); // P - 05 +// usb_tx_createtask(); // P - 04 + gossiper_createtask(); // P - 06 +// ebmp_create_task(); +// watchdog_create_task(); + + /* Initialize subsystem interface to set up interfaces and launch + * subsystem tasks */ + SSRegistry_init(); + +} + +/***************************************************************************** + ** FUNCTION NAME : bigbrother_init + ** + ** DESCRIPTION : Initializes the Big Brother task. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void bigbrother_init(void) +{ + /*Creating Semaphore for RX Message Queue*/ + semBigBrotherMsg = Semaphore_create(0, NULL, NULL); + if (semBigBrotherMsg == NULL) { + LOGGER_ERROR("BIGBROTHER:ERROR::BIGBROTHER RX Semaphore creation failed.\n"); + } + /*Creating RX Message Queue*/ + bigBrotherRxMsgQueue = Util_constructQueue(&bigBrotherRxMsg); + LOGGER_DEBUG("BIGBROTHER:INFO::Constructing message Queue for 0x%x Big Brother RX Messages.\n", + bigBrotherRxMsgQueue); + + /*Creating TX Message Queue*/ + bigBrotherTxMsgQueue = Util_constructQueue(&bigBrotherTxMsg); + LOGGER_DEBUG("BIGBROTHER:INFO::Constructing message Queue for 0x%x Big Brother RX Messages.\n", + bigBrotherTxMsgQueue); +} + +/***************************************************************************** + ** FUNCTION NAME : bigbrother_taskfxn + ** + ** DESCRIPTION : handles the system state and subsystem states. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void bigbrother_taskfxn(UArg a0, UArg a1) +{ + bigbrother_init(); + + /* Initialize GPIO Expander SX1509 */ + bigbrother_ioexp_init(); +// hci_buzzer_beep(1); + + //Create Tasks. + bigborther_spwan_task(); + //Perform POST + bigborther_initiate_post(); + while (true) { + if (Semaphore_pend(semBigBrotherMsg, BIOS_WAIT_FOREVER)) { + while (!Queue_empty(bigBrotherRxMsgQueue)) { + uint8_t *pWrite = (uint8_t *) Util_dequeueMsg( + bigBrotherRxMsgQueue); + if (pWrite) { + bigbrother_process_rx_msg(pWrite); + } + } + while (!Queue_empty(bigBrotherTxMsgQueue)) { + uint8_t *pWrite = (uint8_t *) Util_dequeueMsg( + bigBrotherTxMsgQueue); + if (pWrite) { + bigbrother_process_tx_msg(pWrite); + } + } + } + } +} + +/***************************************************************************** + ** FUNCTION NAME : bigbrother_createtask + ** + ** DESCRIPTION : Creates task for Big Brother task + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void bigbrother_createtask(void) +{ + //watchdog_pin(); + Task_Params taskParams; + // Configure task + Task_Params_init(&taskParams); + taskParams.stack = bigBrotherTaskStack; + taskParams.stackSize = BIGBROTHER_TASK_STACK_SIZE; + taskParams.priority = BIGBROTHER_TASK_PRIORITY; + Task_construct(&bigBrotherTask, bigbrother_taskfxn, &taskParams, NULL); + LOGGER_DEBUG("BIGBROTHER:INFO::Creating a BigBrother task.\n"); +} diff --git a/firmware/src/comm/gossiper.c b/firmware/src/comm/gossiper.c new file mode 100644 index 0000000000..45e2a6e695 --- /dev/null +++ b/firmware/src/comm/gossiper.c @@ -0,0 +1,248 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include "comm/gossiper.h" +#include "common/inc/global/ocmp_frame.h" +#include "inc/common/bigbrother.h" +#include "inc/common/global_header.h" +#include "inc/interfaces/uartdma.h" + +#include +#include + +/***************************************************************************** + * HANDLES DEFINITION + *****************************************************************************/ +// Semaphore on which USB will listen. +//extern Semaphore_Handle semUSBTX; + +/* + * usbTxMsgQueue - Message queue of USB interface to read. + * Will be filled in by Gossiper. + */ +//extern Queue_Handle usbTxMsgQueue; + +//extern Semaphore_Handle ethTxsem; +//extern Queue_Handle ethTxMsgQueue; + +/* Config message Queue */ +/* + * This is the semaphore posted by either Interface (UART/Ethernet) when it has + * recieved from external world or Bigbrother task once frame is processed and + * needs to be sent back. + */ +Semaphore_Handle semGossiperMsg; + +// Queue object +static Queue_Struct gossiperRxMsg; +static Queue_Struct gossiperTxMsg; + +/* + * gossiperRxMsgQueue - Queue used by the Interface to + * send data to Gossiper + */ +Queue_Handle gossiperRxMsgQueue; + +/* + * gossiperTxMsgQueue - Queue used by the Big brother to + * forward data to Gossiper + */ +Queue_Handle gossiperTxMsgQueue; + +/* Global Task Configuration Variables */ +static Task_Struct gossiperTask; +static Char gossiperTaskStack[GOSSIPER_TASK_STACK_SIZE]; + +/***************************************************************************** + * FUNCTION DECLARATIONS + *****************************************************************************/ +static void gossiper_init(); +static void gossiper_taskfxn(UArg a0, UArg a1); +static ReturnStatus gossiper_process_rx_msg(uint8_t *pMsg); +static ReturnStatus gossiper_process_tx_msg(uint8_t *pMsg); +static ReturnStatus gossiper_uart_send_msg(uint8_t *pMsg); + +/***************************************************************************** + ** FUNCTION NAME : gossiper_createtask + ** + ** DESCRIPTION : Creates task for Gossiper + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void gossiper_createtask(void) +{ + Task_Params taskParams; + // Configure task + Task_Params_init(&taskParams); + taskParams.stack = gossiperTaskStack; + taskParams.stackSize = GOSSIPER_TASK_STACK_SIZE; + taskParams.priority = GOSSIPER_TASK_PRIORITY; + Task_construct(&gossiperTask, gossiper_taskfxn, &taskParams, NULL); + LOGGER_DEBUG("GOSSIPER:INFO::Creating a Gossiper task.\n"); +} + +/***************************************************************************** + ** FUNCTION NAME : gossiper_init + ** + ** DESCRIPTION : Initializes the gossiper task. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void gossiper_init(void) +{ + /*Creating Semaphore for RX Message Queue*/ + semGossiperMsg = Semaphore_create(0, NULL, NULL); + if (semGossiperMsg == NULL) { + LOGGER_ERROR("GOSSIPER:ERROR::GOSSIPER RX Semaphore creation failed.\n"); + } + + /*Creating RX Message Queue*/ + gossiperRxMsgQueue = Util_constructQueue(&gossiperRxMsg); + LOGGER_DEBUG("GOSSIPER:INFO::Constructing message Queue 0x%x for RX Gossiper Messages.\n", + gossiperRxMsgQueue); + + /*Creating TX Message Queue*/ + gossiperTxMsgQueue = Util_constructQueue(&gossiperTxMsg); + LOGGER_DEBUG("GOSSIPER:INFO::Constructing message Queue 0x%x for TX Gossiper Messages.\n", + gossiperTxMsgQueue); +} + +/***************************************************************************** + ** FUNCTION NAME : gossiper_taskfxn + ** + ** DESCRIPTION : Recieve and transmitts media depenedent messages. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void gossiper_taskfxn(UArg a0, UArg a1) +{ + gossiper_init(); + while (true) { + if (Semaphore_pend(semGossiperMsg, BIOS_WAIT_FOREVER)) { + /* Gossiper RX Messgaes */ + while (!Queue_empty(gossiperRxMsgQueue)) { + uint8_t *pWrite = (uint8_t *) Util_dequeueMsg( + gossiperRxMsgQueue); + if (pWrite) { + gossiper_process_rx_msg(pWrite); + } else { + LOGGER_ERROR("GOSSIPER::ERROR:: No Valid Pointer.\n"); + } + } + + /* Gossiper TX Messgaes */ + while (!Queue_empty(gossiperTxMsgQueue)) { + uint8_t *pWrite = (uint8_t *) Util_dequeueMsg( + gossiperTxMsgQueue); + if (pWrite) { + gossiper_process_tx_msg(pWrite); + } else { + LOGGER_ERROR("GOSSIPER::ERROR:: No Valid Pointer.\n"); + } + } + } + } +} + +/***************************************************************************** + ** FUNCTION NAME : gossiper_process_rx_msg + ** + ** DESCRIPTION : Processes the RX Gossiper messages + ** + ** ARGUMENTS : Pointer to message structure + ** + ** RETURN TYPE : RETURN_OK or RETURN_NOTOK + ** + *****************************************************************************/ +static ReturnStatus gossiper_process_rx_msg(uint8_t *pMsg) +{ + ReturnStatus status = RETURN_OK; + LOGGER_DEBUG("GOSSIPER:INFO:: Processing Gossiper RX Message.\n"); + + OCMPMessageFrame * pOCMPMessageFrame = (OCMPMessageFrame *) pMsg; + if (pOCMPMessageFrame != NULL) { + LOGGER_DEBUG("GOSSIPER:INFO:: RX Msg recieved with Length: 0x%x, Interface: 0x%x, Seq.No: 0x%x, TimeStamp: 0x%x.\n", + pOCMPMessageFrame->header.ocmpFrameLen, + pOCMPMessageFrame->header.ocmpInterface, + pOCMPMessageFrame->header.ocmpSeqNumber, + pOCMPMessageFrame->header.ocmpTimestamp); + /*Update the Debug info required based on the debug jumper connected*/ + //status = CheckDebugEnabled() + if (pOCMPMessageFrame->message.msgtype == OCMP_MSG_TYPE_DEBUG) { +#if 0 + if (!IN_DEBUGMODE()) { + // If board is not set in debug mode then discard the message. + } else { + pOCMPMessageFrame->message.msgtype = UNSET_DEBUG_MODE( + pOCMPMessageFrame->message.msgtype); + } +#endif + } + Util_enqueueMsg(bigBrotherRxMsgQueue, semBigBrotherMsg, (uint8_t*) pMsg); + } else { + LOGGER_ERROR("GOSSIPER:ERROR:: Not valid pointer.\n"); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : gossiper_process_tx_msg + ** + ** DESCRIPTION : Processes the Gossiper TX Messages + ** + ** ARGUMENTS : Pointer to message structure + ** + ** RETURN TYPE : RETURN_OK or RETURN_NOTOK + ** + *****************************************************************************/ +static ReturnStatus gossiper_process_tx_msg(uint8_t *pMsg) +{ + ReturnStatus status = RETURN_OK; + LOGGER_DEBUG("GOSSIPER:INFO:: Processing Gossiper TX Message.\n"); + OCMPMessageFrame * pOCMPMessageFrame = (OCMPMessageFrame *) pMsg; + if (pOCMPMessageFrame != NULL) { + status = gossiper_uart_send_msg(pMsg); + } else { + LOGGER_ERROR("BIGBROTHER:ERROR:: Not valid pointer.\n"); + } + return status; +} +/***************************************************************************** + ** FUNCTION NAME : gossiper_uart_send_msg + ** + ** DESCRIPTION : transmitt TX Messages to UART + ** + ** ARGUMENTS : Pointer to message + ** + ** RETURN TYPE : RETURN_OK or RETURN_NOTOK + ** + *****************************************************************************/ +static ReturnStatus gossiper_uart_send_msg(uint8_t *pMsg) +{ + ReturnStatus status = RETURN_OK; + LOGGER_DEBUG("GOSSIPER:INFO:: Forwarding TX message to the UART Interface.\n"); + if (pMsg != NULL) { + Util_enqueueMsg(uartTxMsgQueue, semUARTTX, (uint8_t*) pMsg); + } else { + LOGGER_ERROR("GOSSIPER::ERROR::No Valid Pointer.\n"); + } + return status; +} diff --git a/firmware/src/comm/gossiper.h b/firmware/src/comm/gossiper.h new file mode 100644 index 0000000000..1fd6672765 --- /dev/null +++ b/firmware/src/comm/gossiper.h @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#ifndef GOSSIPER_H_ +#define GOSSIPER_H_ + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include "inc/utils/util.h" + +/***************************************************************************** + * MACRO DEFINITIONS + *****************************************************************************/ +#define GOSSIPER_TASK_PRIORITY 6 +#define GOSSIPER_TASK_STACK_SIZE 2048 + +#define SET_DEBEUG_MODE(debugMode) ((debugMode | 0x00)) +#define UNSET_DEBUG_MODE(debugMode) ((debugMode & 0x0f)) + +/***************************************************************************** + * HANDLE DEFINITIONS + *****************************************************************************/ +/* Semaphore and Queue Handles for Gossiper */ +extern Semaphore_Handle semGossiperMsg; +extern Queue_Handle gossiperTxMsgQueue; +extern Queue_Handle gossiperRxMsgQueue; + +/***************************************************************************** + * FUNCTION DECLARATIONS + *****************************************************************************/ +void gossiper_createtask(void); + +#endif /* GOSSIPER_H_ */ diff --git a/firmware/src/devices/eeprom.c b/firmware/src/devices/eeprom.c new file mode 100644 index 0000000000..0ef0a88f40 --- /dev/null +++ b/firmware/src/devices/eeprom.c @@ -0,0 +1,267 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** +#include "Board.h" +#include "inc/common/global_header.h" +#include "inc/common/byteorder.h" +#include "inc/devices/eeprom.h" + +#include +#ifndef UT_FRAMEWORK +#include /* TODO: for htons - clean up this random include */ +#endif +#include + +#define WP_ASSERT 1 +#define WP_DEASSERT 0 + +extern Eeprom_Cfg eeprom_psu_sid; +extern Eeprom_Cfg eeprom_psu_inv; + +static ReturnStatus i2c_eeprom_write(I2C_Handle i2cHandle, + uint8_t deviceAddress, + uint16_t regAddress, + const void *value, + size_t numofBytes); + +static ReturnStatus i2c_eeprom_read(I2C_Handle i2cHandle, + uint16_t deviceAddress, + uint16_t regAddress, + void *value, + size_t numofbytes); + +/***************************************************************************** + ** FUNCTION NAME : eeprom_init + ** + ** DESCRIPTION : Initialize an EEPROM device (WP gpio / test read) + ** + ** ARGUMENTS : EEPROM config + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +bool eeprom_init(Eeprom_Cfg *cfg) { + /* Configure our WP pin (if any) and set to be low (protected) by default */ + if (cfg->pin_wp) { + OcGpio_configure(cfg->pin_wp, + OCGPIO_CFG_OUTPUT | OCGPIO_CFG_OUT_HIGH); + } + + /* Test communication to the EEPROM */ + uint8_t test_byte; + if (eeprom_read(cfg, 0x00, &test_byte, sizeof(test_byte)) != RETURN_OK) { + return false; + } + + return true; +} + +/***************************************************************************** + ** FUNCTION NAME : eeprom_read + ** + ** DESCRIPTION : Read the values from the EEPROM register. + ** + ** ARGUMENTS : EEPROM (Slave) address, Register address and + ** pointer to value read. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus eeprom_read(Eeprom_Cfg *cfg, + uint16_t address, + void *buffer, + size_t size) +{ + ReturnStatus status = RETURN_OK; + I2C_Handle eepromHandle = i2c_get_handle(cfg->i2c_dev.bus); + if (!eepromHandle) { + LOGGER_ERROR("EEPROM:ERROR:: Failed to get I2C Bus for " + "EEPROM device 0x%x.\n", cfg->i2c_dev.slave_addr); + } else { + /* TODO: if we're concerned about hogging the bus, we could always + * page reads, but this doesn't seem necessary right now + */ + /* TODO: check for out-of-bounds addresses (some EEPROM wrap around + * when reading after the end, so this could lead to confusion) + */ + status = i2c_eeprom_read(eepromHandle, cfg->i2c_dev.slave_addr, + address, buffer, size); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : eeprom_write + ** + ** DESCRIPTION : Write the value to EEPROM register. + ** + ** ARGUMENTS : EEPROM (Slave) address, Register address and value + ** to be written. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus eeprom_write(const Eeprom_Cfg *cfg, + uint16_t address, + const void *buffer, + size_t size) +{ + ReturnStatus status = RETURN_OK; + I2C_Handle eepromHandle = i2c_get_handle(cfg->i2c_dev.bus); + if (!eepromHandle) { + LOGGER_ERROR("EEPROM:ERROR:: Failed to get I2C Bus for " + "EEPROM device 0x%x.\n", cfg->i2c_dev.slave_addr); + } else { + /* Respect EEPROM page size */ + const size_t page_size = cfg->type.page_size; + if (page_size) { + while (size > page_size) { + status = i2c_eeprom_write(eepromHandle, cfg->i2c_dev.slave_addr, + address, buffer, page_size); + + size -= page_size; + address += page_size; + buffer = (const uint8_t *)buffer + page_size; + } + } + status = i2c_eeprom_write(eepromHandle, cfg->i2c_dev.slave_addr, + address, buffer, size); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : i2c_eeprom_write + ** + ** DESCRIPTION : Writing device register over i2c bus. + ** + ** ARGUMENTS : I2C handle, device address, register address and value. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus i2c_eeprom_write(I2C_Handle i2cHandle, + uint8_t slaveAddress, + uint16_t memAddress, + const void *value, + size_t numofBytes) +{ + ReturnStatus status = RETURN_OK; + uint8_t txBuffer[numofBytes + 1]; + + /*TODO: This approach needs to be looked into when same + * codebase is used for PSU and GBC + + uint8_t txBuffer[numofBytes + 2]; + *(uint16_t *)txBuffer = htobe16(memAddress); + memcpy((txBuffer + 2), value, numofBytes); + */ + + *txBuffer = (memAddress & 0xFF); + slaveAddress |= ((memAddress >> 8 ) & 0x01); + memcpy((txBuffer + 1), value, numofBytes); + I2C_Transaction i2cTransaction; + i2cTransaction.slaveAddress = slaveAddress; + i2cTransaction.writeBuf = txBuffer; + i2cTransaction.writeCount = sizeof(txBuffer); + i2cTransaction.readBuf = NULL; + i2cTransaction.readCount = 0; + if (I2C_transfer(i2cHandle, &i2cTransaction)) { + LOGGER_DEBUG("EEPROM:INFO:: I2C write success for device: 0x%x reg Addr: 0x%x\n", + slaveAddress, memAddress); + status = RETURN_OK; + } else { + LOGGER_ERROR("EEPROM:ERROR:: I2C write failed for for device: 0x%x reg Addr: 0x%x\n", + slaveAddress, memAddress); + status = RETURN_NOTOK; + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : i2c_eeprom_read + ** + ** DESCRIPTION : Reading device register over i2c bus. + ** + ** ARGUMENTS : I2C handle, device address, register address and value. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus i2c_eeprom_read(I2C_Handle i2cHandle, + uint16_t slaveAddress, + uint16_t memAddress, + void *value, + size_t numofbytes) +{ + ReturnStatus status = RETURN_OK; +// uint16_t txBuffer = htobe16(memAddress); /* Address is big-endian */ + uint8_t txBuffer = (memAddress & 0xFF); /* Address is big-endian */ + slaveAddress |= ((memAddress >> 8 )& 0x01); + + I2C_Transaction i2cTransaction; + i2cTransaction.slaveAddress = slaveAddress; + i2cTransaction.writeBuf = &txBuffer; + i2cTransaction.writeCount = sizeof(txBuffer); + i2cTransaction.readBuf = value; + i2cTransaction.readCount = numofbytes; + if (I2C_transfer(i2cHandle, &i2cTransaction)) { + LOGGER_DEBUG("EEPROM:INFO:: I2C read success for device: 0x%x reg Addr: 0x%x\n", + slaveAddress, memAddress); + status = RETURN_OK; + } else { + LOGGER_ERROR("EEPROM:ERROR:: I2C write failed for for device: 0x%x reg Addr: 0x%x\n", + slaveAddress, memAddress); + status = RETURN_NOTOK; + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : eeprom_disable_write + ** + ** DESCRIPTION : Read the values from the EEPROM register. + ** + ** ARGUMENTS : EEPROM handle. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus eeprom_disable_write(Eeprom_Cfg *cfg) +{ + if (cfg->pin_wp) { + OcGpio_write(cfg->pin_wp, WP_ASSERT); + } + + /* TODO: error detection */ + return RETURN_OK; +} + +/***************************************************************************** + ** FUNCTION NAME : eeprom_enable_write + ** + ** DESCRIPTION : Enable eeprom write operation. + ** + ** ARGUMENTS : EEPROM handle. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus eeprom_enable_write(Eeprom_Cfg *cfg) +{ + if (cfg->pin_wp) { + OcGpio_write(cfg->pin_wp, WP_DEASSERT); + } + + /* TODO: error detection */ + return RETURN_OK; +} diff --git a/firmware/src/devices/i2c/threaded_int.c b/firmware/src/devices/i2c/threaded_int.c new file mode 100644 index 0000000000..5047cd7282 --- /dev/null +++ b/firmware/src/devices/i2c/threaded_int.c @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#include "threaded_int.h" + +#include "inc/common/global_header.h" + +#include +#include +#include + +// Threaded interrupt info +//#define TI_TASKSTACKSIZE 1024 +#define TI_TASKSTACKSIZE 512 +#define TI_TASKPRIORITY 6 + +// This number is fairly superficial - just used to keep track of the +// various tasks, it can be increased without much overhead +#define MAX_DEVICES 30 + +// Config simply to map context to our GPIO interrupts +typedef struct InterruptConfig { + Semaphore_Handle sem; //!< Semaphore to wake up INT thread + ThreadedInt_Callback cb; //!< Callback to run when interrupt occurs + void *context; //!< Pointer to pass to cb function +} InterruptConfig; +static InterruptConfig s_intConfigs[MAX_DEVICES] = {}; +static int s_numDevices = 0; + +static void gpioIntFxn(const OcGpio_Pin *pin, void *context) { + Semaphore_Handle sem = context; + + // TODO: this should probably be an assert + if (!sem) { + return; + } + + /* Just wake up the TI task */ + Semaphore_post(sem); +} + +static void ThreadedInt_Task(UArg arg0, UArg arg1) { + InterruptConfig *cfg = (InterruptConfig *)arg0; + if (!cfg) { + DEBUG("Threaded Int started without configuration???\n"); + return; + } + + DEBUG("Threaded INT thread ready\n"); + while (true) { + Semaphore_pend(cfg->sem, BIOS_WAIT_FOREVER); + cfg->cb(cfg->context); + } +} + +// TODO: this function isn't thread safe at the moment +void ThreadedInt_Init(OcGpio_Pin *irqPin, ThreadedInt_Callback cb, + void *context) { + /*TODO: stop gap arrangement to prevent spawning tasks for monitoring alerts*/ + // return; + // Build up table of all devices for interrupt handling. This is an ok + // workaround for TI RTOS GPIO interrupts for now (only using one device) + if (s_numDevices >= MAX_DEVICES) { + DEBUG("ThrdInt::FATAL: too many configurations"); + return; + } + int devNum = s_numDevices++; + + Semaphore_Handle sem = Semaphore_create(0, NULL, NULL); + if (!sem) { + DEBUG("ThrdInt::Can't create ISR semaphore\n"); + return; + } + + s_intConfigs[devNum] = (InterruptConfig) { + .sem = sem, + .cb = cb, + .context = context, + }; + + // Start interrupt handling task + // One task per interrupt, not that efficient, but we don't have much + // need to optimize into a thread pool + // TODO: look into error block and see if I should use it + Task_Params taskParams; + Task_Params_init(&taskParams); + taskParams.stackSize = TI_TASKSTACKSIZE; + taskParams.priority = TI_TASKPRIORITY; + taskParams.arg0 = (uintptr_t)&s_intConfigs[devNum]; + Task_Handle task = Task_create(ThreadedInt_Task, &taskParams, NULL); + if (!task) { + DEBUG("ThrdInt::FATAL: Unable to start interrupt task\n"); + Semaphore_delete(&sem); + s_numDevices--; + return; + } + // TODO: what do I do with task handle? + + /* Set up IRQ pin callback */ + OcGpio_setCallback(irqPin, gpioIntFxn, sem); + OcGpio_enableInt(irqPin); +} + diff --git a/firmware/src/devices/i2c/threaded_int.h b/firmware/src/devices/i2c/threaded_int.h new file mode 100644 index 0000000000..891b77587e --- /dev/null +++ b/firmware/src/devices/i2c/threaded_int.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#pragma once + +#ifndef DEVICES_I2C_THREADED_INT_H_ +#define DEVICES_I2C_THREADED_INT_H_ + +#include "drivers/OcGpio.h" + +typedef void (*ThreadedInt_Callback)(void *context); + +void ThreadedInt_Init(OcGpio_Pin *irqPin, ThreadedInt_Callback cb, + void *context); + +#endif /* DEVICES_I2C_THREADED_INT_H_ */ diff --git a/firmware/src/devices/i2cbus.c b/firmware/src/devices/i2cbus.c new file mode 100644 index 0000000000..81c5e95d50 --- /dev/null +++ b/firmware/src/devices/i2cbus.c @@ -0,0 +1,151 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** +#include "Board.h" +#include "helpers/array.h" +#include "inc/common/global_header.h" +#include "inc/common/i2cbus.h" + +/* TI-RTOS driver files */ +#include + +#include + +//***************************************************************************** +// HANDLES DEFINITION +//***************************************************************************** +I2C_Handle OC_I2C_Handle[Board_I2CCOUNT] = {}; + +/***************************************************************************** + ** FUNCTION NAME : i2c_open_bus + ** + ** DESCRIPTION : Initialize I2C Bus + ** + ** ARGUMENTS : I2C bus index + ** + ** RETURN TYPE : I2C_Handle (NULL on failure) + ** + *****************************************************************************/ +I2C_Handle i2c_open_bus(unsigned int index) +{ + if (index >= ARRAY_SIZE(OC_I2C_Handle)) { + LOGGER_ERROR("I2CBUS:ERROR:: I2C bus %d not found\n", index); + return NULL; + } + + I2C_Params i2cParams; + I2C_Params_init(&i2cParams); + i2cParams.bitRate = I2C_400kHz; + + if (!OC_I2C_Handle[index]) { + OC_I2C_Handle[index] = I2C_open(index, &i2cParams); + + if (!OC_I2C_Handle[index]) { + LOGGER_ERROR("I2CBUS:ERROR:: Failed Initializing I2C bus %d.\n", + index); + } + } + return OC_I2C_Handle[index]; +} + +/***************************************************************************** + ** FUNCTION NAME : i2c_close_bus + ** + ** DESCRIPTION : Initialize I2C Bus + ** + ** ARGUMENTS : I2C bus index + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void i2c_close_bus(I2C_Handle* i2cHandle) +{ + I2C_close(*i2cHandle); + i2cHandle = NULL; +} + +/***************************************************************************** + ** FUNCTION NAME : i2c_reg_write + ** + ** DESCRIPTION : Writing device register over i2c bus. + ** + ** ARGUMENTS : I2C handle, device address, register address and value. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus i2c_reg_write( I2C_Handle i2cHandle, + uint8_t deviceAddress, + uint8_t regAddress, + uint16_t value, + uint8_t numofBytes) +{ + ReturnStatus status = RETURN_OK; + uint8_t txBuffer[3]; + I2C_Transaction i2cTransaction; + txBuffer[0] = regAddress; + memcpy(&txBuffer[1],&value,numofBytes); + i2cTransaction.slaveAddress = deviceAddress; + i2cTransaction.writeBuf = txBuffer; + i2cTransaction.writeCount = numofBytes + 1; + i2cTransaction.readBuf = NULL; + i2cTransaction.readCount = 0; + if (I2C_transfer(i2cHandle, &i2cTransaction)) { + //LOGGER_DEBUG("I2CBUS:INFO:: I2C write success for device: 0x%x reg Addr: 0x%x value: 0x%x.\n", + // deviceAddress, regAddress, value); + status = RETURN_OK; + } else { + LOGGER_ERROR("I2CBUS:ERROR:: I2C write failed for for device: 0x%x reg Addr: 0x%x value: 0x%x.\n", + deviceAddress, regAddress, value); + status = RETURN_NOTOK; + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : i2c_reg_read + ** + ** DESCRIPTION : Reading device register over i2c bus. + ** + ** ARGUMENTS : I2C handle, device address, register address and value. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus i2c_reg_read( I2C_Handle i2cHandle, + uint8_t deviceAddress, + uint8_t regAddress, + uint16_t *value, + uint8_t numofBytes) +{ + ReturnStatus status = RETURN_OK; + uint8_t txBuffer[1] = { 0 }; + uint8_t rxBuffer[2] = { 0 }; + txBuffer[0] = regAddress; + I2C_Transaction i2cTransaction; + i2cTransaction.slaveAddress = deviceAddress; + i2cTransaction.writeBuf = &txBuffer; + i2cTransaction.writeCount = 1; + i2cTransaction.readBuf = rxBuffer; + i2cTransaction.readCount = numofBytes; + if (I2C_transfer(i2cHandle, &i2cTransaction)) { + memcpy(value,rxBuffer,numofBytes); + LOGGER_ERROR("I2CBUS:INFO:: I2C read success for device: 0x%x reg Addr: 0x%x value : 0x%x.\n", + deviceAddress, regAddress, *value); + status = RETURN_OK; + } else { + LOGGER_ERROR("I2CBUS:ERROR:: I2C read failed for for device: 0x%x reg Addr: 0x%x.\n", + deviceAddress, regAddress); + status = RETURN_NOTOK; + } + return status; +} diff --git a/firmware/src/devices/ina226.c b/firmware/src/devices/ina226.c new file mode 100644 index 0000000000..fad9e080fd --- /dev/null +++ b/firmware/src/devices/ina226.c @@ -0,0 +1,596 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** +#include "devices/i2c/threaded_int.h" +#include "inc/common/byteorder.h" +#include "inc/common/global_header.h" +#include "inc/devices/ina226.h" +#include "helpers/memory.h" + +/***************************************************************************** + * REGISTER DEFINITIONS + *****************************************************************************/ +#define INA_CONFIGURATION_REG 0x00 +#define INA_SHUNTVOLTAGE_REG 0x01 +#define INA_BUSVOLTAGE_REG 0x02 +#define INA_POWER_REG 0x03 +#define INA_CURRENT_REG 0x04 +#define INA_CALIBRATION_REG 0x05 +#define INA_MASKENABLE_REG 0x06 +#define INA_ALERTLIMIT_REG 0x07 +#define INA_MANUFACTUREID_REG 0xFE +#define INA_DIEID_REG 0xFF + +/*INA226 Device Info */ +#define INA226_MANFACTURE_ID 0x5449 +#define INA226_DEVICE_ID 0x2260 +#define INA226_DEV_VERSION 0x00 + +/* Configuration Register Bits */ +#define INA_CFG_RESET (1 << 15) + +/* + * Conversion of current into Shunt Voltage Register contents and viceversa. + * + * First Calculate the Current Register Value from the given Current Value + * ui16rfINARegValue = ui16rfINACurrentLimit/(INA226_CURRENT_LSB); + * Calculate Shunt Voltage Alert Limit Register Value + * ui16rfINARegValue = (ui16rfINARegValue * 2048)/INA226_CALIBRATION_REG_VALUE; + */ +#define CURRENT_TO_REG(x) ((2048 *(x/INA226_CURRENT_LSB)/INA226_CAL_REG_VALUE)) +#define REG_TO_CURRENT(y) ((y * INA226_CURRENT_LSB * INA226_CAL_REG_VALUE)/2048) + +/***************************************************************************** + * CONSTANTS DEFINITIONS + *****************************************************************************/ +/* INA226 LSB Values */ +#define INA226_VSHUNT_LSB 2.5 /* 2.5uV or 2500nV (uV default) */ +#define INA226_VBUS_LSB 1.25 /* 1.25mV or 1250uV (mV default) */ +//#define INA226_CURRENT_LSB 0.1 /* 0.100mA 0r 100uA (mA default) */ +#define INA226_CURRENT_LSB 0.00205 /* 0.100mA 0r 100uA (mA default) */ +#define INA226_POWER_LSB 2.5 /* 2.5mW or 2500uW (mW default) */ + +/* Configure the Configuration register with Number of Samples and Conversion + * Time for Shunt and Bus Voltage. + * Min(Default):0x4127; Max: 0x4FFF; Average: 0x476F + */ +#define INA226_CONFIG_REG_VALUE 0x476F + +/* Configure Calibration register with shunt resistor value and current LSB. + Current_LSB = Maximum Expected Current/2^15 + Current_LSB = 2A/2^15 = 0.00006103515625 = 61uA ~ 100uA(Maximum Expected Current = 2A) + Calibration Register(CAL) = 0.00512/(Current_LSB*RSHUNT) + CAL = 0.00512/(100uA*2mOhm) = = 25600 = 0x6400.(RSHUNT = 2mohm) + */ +//#define INA226_CAL_REG_VALUE 0x6400 +#define INA226_CAL_REG_VALUE 0xA3D7 + +#define INA226_MASKEN_REG_VALUE 0x8001 + +extern __attribute__((weak)) OcGpio_Port ec_io; + +void watchdog_pin() +{ + OcGpio_Pin pin_watchdog = { &ec_io, 2 }; + + const uint32_t pin_evt_cfg = 0; + OcGpio_configure(&pin_watchdog, pin_evt_cfg); + while(1) { + OcGpio_write(&pin_watchdog, true); + //Task_sleep(4*500); + OcGpio_write(&pin_watchdog, false); + } +return; +} +/***************************************************************************** + ** FUNCTION NAME : read_ina_reg + ** + ** DESCRIPTION : Read a 16 bit value from INA226 register. + ** + ** ARGUMENTS : i2c device, Register address and value + ** to be read. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus read_ina_reg(const INA226_Dev *dev, + uint8_t regAddress, + uint16_t *regValue) +{ + ReturnStatus status = RETURN_NOTOK; + I2C_Handle inaHandle = i2c_get_handle(dev->cfg.dev.bus); + if (!inaHandle) { + LOGGER_ERROR("INASENSOR:ERROR:: Failed to get I2C Bus for INA sensor " + "0x%x on bus 0x%x.\n", dev->cfg.dev.slave_addr, + dev->cfg.dev.bus); + } else { + status = i2c_reg_read(inaHandle, dev->cfg.dev.slave_addr, regAddress, + regValue, 2); + *regValue = betoh16(*regValue); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : write_ina_reg + ** + ** DESCRIPTION : Write 16 bit value to INA226 register. + ** + ** ARGUMENTS : i2c device, Register address and value + ** to be written. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus write_ina_reg(const INA226_Dev *dev, + uint8_t regAddress, + uint16_t regValue) +{ + ReturnStatus status = RETURN_NOTOK; + I2C_Handle inaHandle = i2c_get_handle(dev->cfg.dev.bus); + if (!inaHandle) { + LOGGER_ERROR("INASENSOR:ERROR:: Failed to get I2C Bus for INA sensor " + "0x%x on bus 0x%x.\n", dev->cfg.dev.slave_addr, + dev->cfg.dev.bus); + } else { + regValue = htobe16(regValue); + status = i2c_reg_write(inaHandle, dev->cfg.dev.slave_addr, regAddress, + regValue, 2); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_get_dev_id + ** + ** DESCRIPTION : Read the device id of Current sensor. + ** + ** ARGUMENTS : i2c device and pointer to device Id. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus ina226_getDevId(INA226_Dev *dev, uint16_t *devID) +{ + return read_ina_reg(dev, INA_DIEID_REG, devID); +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_get_mfg_id + ** + ** DESCRIPTION : Read the mfg id of Current sensor. + ** + ** ARGUMENTS : i2c device and out-pointer to manufacturing ID. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus ina226_getMfgId(INA226_Dev *dev, uint16_t *mfgID) +{ + return read_ina_reg(dev, INA_MANUFACTUREID_REG, mfgID); +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_set_cfg_reg + ** + ** DESCRIPTION : Write the value to Current sensor configuration + ** register. + ** + ** ARGUMENTS : i2c device and new value of configuration register. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus _set_cfg_reg(INA226_Dev *dev, uint16_t regValue) +{ + return write_ina_reg(dev, INA_CONFIGURATION_REG, regValue); +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_set_cal_reg + ** + ** DESCRIPTION : Write the value to Current sensor calibration register. + ** + ** ARGUMENTS : i2c device and new value of calibration register. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus _set_cal_reg(INA226_Dev *dev, uint16_t regValue) +{ + return write_ina_reg(dev, INA_CALIBRATION_REG, regValue); +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_get_curr_limit + ** + ** DESCRIPTION : Read the value of Current sensor alert limit register. + ** + ** ARGUMENTS : i2c device and out-pointer to current limit. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ina226_readCurrentLim(INA226_Dev *dev, uint16_t* currLimit) +{ + uint16_t regValue = 0x0000; + ReturnStatus status = read_ina_reg(dev, INA_ALERTLIMIT_REG, ®Value); + if (status == RETURN_OK) { + *currLimit = REG_TO_CURRENT(regValue); + LOGGER_DEBUG("INASENSOR:INFO:: INA sensor 0x%x on bus 0x%x is " + "reporting current limit of %d mA.\n", + dev->cfg.dev.slave_addr, dev->cfg.dev.bus, *currLimit); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_set_curr_limit + ** + ** DESCRIPTION : Write the value to Current sensor alert limit register. + ** + ** ARGUMENTS : i2c device and new current limit. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ina226_setCurrentLim(INA226_Dev *dev, uint16_t currLimit) +{ + uint16_t regValue = CURRENT_TO_REG(currLimit); + return write_ina_reg(dev, INA_ALERTLIMIT_REG, regValue); +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_read_alert_reg + ** + ** DESCRIPTION : Read the value to Current sensor mask/enable register. + ** + ** ARGUMENTS : i2c device and out-pointer to enable and mask bits. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus _read_alert_reg(INA226_Dev *dev, uint16_t* regValue) +{ + return read_ina_reg(dev, INA_MASKENABLE_REG, regValue); +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_enable_alert + ** + ** DESCRIPTION : Write the value to Current sensor mask/enable register. + ** + ** ARGUMENTS : i2c device and alert to be enabled. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus _enable_alert(INA226_Dev *dev, uint16_t regValue) +{ + return write_ina_reg(dev, INA_MASKENABLE_REG, regValue); +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_get_bus_volt_value + ** + ** DESCRIPTION : Read the value of Current sensor bus voltage value. + ** + ** ARGUMENTS : i2c device and out-pointer to bus voltage value. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ina226_readBusVoltage(INA226_Dev *dev, + uint16_t* busVoltValue) +{ + uint16_t regValue; + ReturnStatus status = read_ina_reg(dev, INA_BUSVOLTAGE_REG, ®Value); + + if (status == RETURN_OK) { + *busVoltValue = regValue * INA226_VBUS_LSB; + LOGGER_DEBUG("INASENSOR:INFO:: INA sensor 0x%x on bus 0x%x is " + "reporting bus voltage value of %d mV.\n", + dev->cfg.dev.slave_addr, dev->cfg.dev.bus, *busVoltValue); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_get_shunt_volt_value + ** + ** DESCRIPTION : Read the value of Current sensor shunt voltage value. + ** + ** ARGUMENTS : i2c device and out-pointer to shunt voltage. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ina226_readShuntVoltage(INA226_Dev *dev, + uint16_t* shuntVoltValue) +{ + uint16_t regValue; + ReturnStatus status = read_ina_reg(dev, INA_SHUNTVOLTAGE_REG, ®Value); + + if (status == RETURN_OK) { + *shuntVoltValue = regValue * INA226_VSHUNT_LSB; + LOGGER_DEBUG("INASENSOR:INFO:: INA sensor 0x%x on bus 0x%x is " + "reporting shunt voltage value of %d uV.\n", + dev->cfg.dev.slave_addr, dev->cfg.dev.bus, *shuntVoltValue); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_get_curr_value + ** + ** DESCRIPTION : Read the value of Current sensor current value. + ** + ** ARGUMENTS : i2c device and out-pointer to current value. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ina226_readCurrent(INA226_Dev *dev, uint16_t* currValue) +{ + uint16_t regValue; + ReturnStatus status = read_ina_reg(dev, INA_CURRENT_REG, ®Value); + + if (status == RETURN_OK) { + *currValue = regValue * INA226_CURRENT_LSB; + LOGGER_DEBUG("INASENSOR:INFO:: INA sensor 0x%x on bus 0x%x " + "is reporting current value of %d mA.\n", + dev->cfg.dev.slave_addr, dev->cfg.dev.bus, *currValue); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : curr_sens_get_power_value + ** + ** DESCRIPTION : Read the value of Current sensor power value. + ** + ** ARGUMENTS : i2c device and out-pointer to power value. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ina226_readPower(INA226_Dev *dev, uint16_t* powValue) +{ + uint16_t regValue; + ReturnStatus status = read_ina_reg(dev, INA_POWER_REG, ®Value); + if (status == RETURN_OK) { + *powValue = regValue * INA226_POWER_LSB; + LOGGER_DEBUG("INASENSOR:INFO:: INA sensor 0x%x on bus 0x%x is " + "reporting power value of %d mV.\n", + dev->cfg.dev.slave_addr, dev->cfg.dev.bus, *powValue); + } + return status; +} + +/***************************************************************************** + * Internal IRQ handler - reads in triggered interrupts and dispatches CBs + *****************************************************************************/ +static void _ina226_isr(void *context) { + INA226_Dev *dev = context; + + /* Read the alert mask register (will clear the alert bit if set) */ + /* TODO: this seems to be a strange bug in the sensor - sometimes it returns + * 0xFF as the lo-byte. If this occurs, we need to re-read it. + * NOTE: 0x1F is a perfectly legal value, but bits 5-9 are RFU and + * normally zero */ + uint16_t alert_mask = 0xFFFF; + while (LOBYTE(alert_mask) == 0xFF) { + if (_read_alert_reg(dev, &alert_mask) != RETURN_OK) { + LOGGER_DEBUG("INA226:ERROR:: INT mask read failed\n"); + return; + } + } + + if (!dev->obj.alert_cb) { + return; + } + + if (alert_mask & INA_MSK_AFF) { + /* This alert was caused by a fault */ + + /* Theory of operation: After reading the alert, we change the alert + * mask to look at the complement alert. For example, after getting a + * bus over-voltage alert, we switch the mask to tell us when it's + * under-voltage and thus back in operating limits so that it's less + * likely that we'll miss alerts on the shared line, although not + * guaranteed :( */ + + /* The device can only monitor one metric at a time, if multiple flags + * are set, it monitors the highest order bit, so check the config + * in order, from MSB to LSB */ + uint16_t value; + uint16_t new_mask = alert_mask & (~INA_ALERT_EN_MASK); + INA226_Event evt; + uint16_t alert_lim; + ina226_readCurrentLim(dev, &alert_lim); + + if (alert_mask & INA_MSK_SOL) { + if (dev->obj.evt_to_monitor == INA226_EVT_COL || + dev->obj.evt_to_monitor == INA226_EVT_CUL) { + if (ina226_readCurrent(dev, &value) != RETURN_OK) { + value = UINT16_MAX; + } + alert_lim -= INA_HYSTERESIS; + evt = INA226_EVT_COL; + } else { + if (ina226_readShuntVoltage(dev, &value) != RETURN_OK) { + value = UINT16_MAX; + } + evt = INA226_EVT_SOL; + } + new_mask |= INA_MSK_SUL; + } else if (alert_mask & INA_MSK_SUL) { + if (dev->obj.evt_to_monitor == INA226_EVT_CUL || + dev->obj.evt_to_monitor == INA226_EVT_COL) { + if (ina226_readCurrent(dev, &value) != RETURN_OK) { + value = UINT16_MAX; + } + alert_lim += INA_HYSTERESIS; + evt = INA226_EVT_CUL; + } else { + if (ina226_readShuntVoltage(dev, &value) != RETURN_OK) { + value = UINT16_MAX; + } + evt = INA226_EVT_SUL; + } + new_mask |= INA_MSK_SOL; + } else if (alert_mask & INA_MSK_BOL) { + if (ina226_readBusVoltage(dev, &value) != RETURN_OK) { + value = UINT16_MAX; + } + evt = INA226_EVT_BOL; + new_mask |= INA_MSK_BUL; + } else if (alert_mask & INA_MSK_BUL) { + if (ina226_readBusVoltage(dev, &value) != RETURN_OK) { + value = UINT16_MAX; + } + evt = INA226_EVT_BUL; + new_mask |= INA_MSK_BOL; + } else if (alert_mask & INA_MSK_POL) { + if (ina226_readPower(dev, &value) != RETURN_OK) { + value = UINT16_MAX; + } + evt = INA226_EVT_POL; + /* TODO: there isn't a PUL alert, not sure what to do here. We + * don't currently use this alert, but it would be nice to have a + * complete driver */ + new_mask |= INA_MSK_POL; + } else { + LOGGER_ERROR("INA226:Unknown alert type\n"); + return; + } + + /* Set a new limit in order to account for hysteresis */ + /* TODO: make this work for all alert types (this is a hack) */ + ina226_setCurrentLim(dev, alert_lim); + + /* Invert the alert type we're looking for */ + if (_enable_alert(dev, new_mask) != RETURN_OK) { + /* TODO [HACK]: this sometimes reports failures at random times, so + * this is a hacked together retry to keep things stable*/ + _enable_alert(dev, new_mask); + } + + dev->obj.alert_cb(evt, value, dev->obj.cb_context); + } + /* TODO: Conversion ready not handled */ +} + +/***************************************************************************** + *****************************************************************************/ +ReturnStatus ina226_init(INA226_Dev *dev) +{ + ReturnStatus status; + dev->obj = (INA226_Obj){}; + + /* Perform a device reset to be safe */ + status = _set_cfg_reg(dev, INA_CFG_RESET); + if (status != RETURN_OK) { + return status; + } + + /* Configure the Configuration register with number of samples and + * conversion time for shunt and bus voltage */ + status = _set_cfg_reg(dev, INA226_CONFIG_REG_VALUE); + if (status != RETURN_OK) { + return status; + } + + /* Configure the Calibration register with shunt resistor value and + * current LSB */ + status = _set_cal_reg(dev, INA226_CAL_REG_VALUE); + if (status != RETURN_OK) { + return status; + } + + /* Make sure we're talking to the right device */ +// if (ina226_probe(dev) != POST_DEV_FOUND) { +// return RETURN_NOTOK; +// } + + if (dev->cfg.pin_alert) { + const uint32_t pin_evt_cfg = OCGPIO_CFG_INPUT | OCGPIO_CFG_INT_FALLING; + if (OcGpio_configure(dev->cfg.pin_alert, pin_evt_cfg) < OCGPIO_SUCCESS) { + return RETURN_NOTOK; + } + + /* Use a threaded interrupt to handle IRQ */ + // ThreadedInt_Init(dev->cfg.pin_alert, _ina226_isr, (void *)dev); + } + return RETURN_OK; +} + +/***************************************************************************** + *****************************************************************************/ +void ina226_setAlertHandler(INA226_Dev *dev, INA226_CallbackFn alert_cb, + void *cb_context) { + dev->obj.alert_cb = alert_cb; + dev->obj.cb_context = cb_context; +} + +/***************************************************************************** + *****************************************************************************/ +ReturnStatus ina226_enableAlert(INA226_Dev *dev, INA226_Event evt) +{ + /* TODO: perhaps caching the mask is better? If we have an active alert, + * we'll inadvertently clear it here */ + /* TODO: this isn't thread safe, but does it need to be? */ + + uint16_t alert_mask; + ReturnStatus res = _read_alert_reg(dev, &alert_mask); + if (res != RETURN_OK) { + return res; + } + + alert_mask &= (~INA_ALERT_EN_MASK); /* Wipe out previous alert EN bits */ + //alert_mask |= (INA_MSK_LEN); /* Enable latch mode (never miss an alert) */ + dev->obj.evt_to_monitor = evt; + switch (evt) { + case INA226_EVT_COL: + alert_mask |= INA_MSK_SOL; + break; + case INA226_EVT_CUL: + alert_mask |= INA_MSK_SUL; + break; + default: + alert_mask |= evt; + } + return _enable_alert(dev, alert_mask); +} + +/***************************************************************************** + *****************************************************************************/ +ePostCode ina226_probe(INA226_Dev *dev, POSTData *postData) +{ + uint16_t devId = 0x00; + uint16_t manfId = 0x0000; + if (ina226_getDevId(dev, &devId) != RETURN_OK) { + return POST_DEV_MISSING; + } + if (devId != INA226_DEVICE_ID) { + return POST_DEV_ID_MISMATCH; + } + + if (ina226_getMfgId(dev, &manfId) != RETURN_OK) { + return POST_DEV_MISSING; + } + if (manfId != INA226_MANFACTURE_ID) { + return POST_DEV_ID_MISMATCH; + } + post_update_POSTData(postData, dev->cfg.dev.bus, dev->cfg.dev.slave_addr,manfId, devId); + return POST_DEV_FOUND; +} diff --git a/firmware/src/devices/ltc4015.c b/firmware/src/devices/ltc4015.c new file mode 100644 index 0000000000..59afbcdd56 --- /dev/null +++ b/firmware/src/devices/ltc4015.c @@ -0,0 +1,604 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** +#include "devices/i2c/threaded_int.h" +#include "inc/common/byteorder.h" +#include "inc/devices/ltc4015.h" +#include "helpers/math.h" +#include "helpers/memory.h" +#include "ltc4015_registers.h" + +#include +#include /* For abort() */ + +#define WTF abort() + +static ReturnStatus LTC4015_reg_write(const LTC4015_Dev *dev, + uint8_t regAddress, + uint16_t regValue) +{ + ReturnStatus status = RETURN_NOTOK; + I2C_Handle battHandle = i2c_get_handle(dev->cfg.i2c_dev.bus); + if (!battHandle) { + LOGGER_ERROR("LTC4015:ERROR:: Failed to open I2C bus for battery " + "charge controller 0x%x.\n", dev->cfg.i2c_dev.slave_addr); + } else { + regValue = htole16(regValue); + status = i2c_reg_write(battHandle, dev->cfg.i2c_dev.slave_addr, + regAddress, regValue, 2); + } + return status; +} + +static ReturnStatus LTC4015_reg_read(const LTC4015_Dev *dev, + uint8_t regAddress, + uint16_t *regValue) +{ + ReturnStatus status = RETURN_NOTOK; + I2C_Handle battHandle = i2c_get_handle(dev->cfg.i2c_dev.bus); + if (!battHandle) { + LOGGER_ERROR("LTC4015:ERROR:: Failed to open I2C bus for battery " + "charge controller 0x%x.\n", dev->cfg.i2c_dev.slave_addr); + } else { + status = i2c_reg_read(battHandle, dev->cfg.i2c_dev.slave_addr, + regAddress, regValue, 2); + *regValue = letoh16(*regValue); + } + return status; +} + +ReturnStatus LTC4015_cfg_icharge(LTC4015_Dev *dev, + uint16_t max_chargeCurrent) // milliAmps +{ + /* Maximum charge current target = (ICHARGE_TARGET + 1) * 1mV/RSNSB + => ICHARGE_TARGET = (target*RSNSB/1mV)-1 */ + int icharge_target = round((max_chargeCurrent * dev->cfg.r_snsb) / 1000.0) + - 1; + icharge_target = MAX(0, icharge_target); + return LTC4015_reg_write(dev, LTC4015_ICHARGE_TARGET_SUBADDR, + icharge_target); +} + +ReturnStatus LTC4015_get_cfg_icharge(LTC4015_Dev *dev, + uint16_t *max_chargeCurrent) // milliAmps +{ + /* Maximum charge current target = (ICHARGE_TARGET + 1) * 1mV/RSNSB */ + uint16_t ichargeCurrent = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, + LTC4015_ICHARGE_TARGET_SUBADDR, + &ichargeCurrent); + *max_chargeCurrent = (ichargeCurrent + 1) * 1000 / dev->cfg.r_snsb; + return status; +} + +ReturnStatus LTC4015_cfg_vcharge(LTC4015_Dev *dev, + uint16_t charge_voltageLevel) // millivolts +{ + /* See datasheet, page 61:VCHARGE_SETTING */ + const double target_v = charge_voltageLevel / (1000.0 * dev->cfg.cellcount); + double vchargeSetting; + switch (dev->cfg.chem) { + case LTC4015_CHEM_LEAD_ACID: + vchargeSetting = round((target_v - 2.0) * 105.0); + break; + case LTC4015_CHEM_LI_FE_PO4: + vchargeSetting = round((target_v - 3.4125) * 80.0); + break; + case LTC4015_CHEM_LI_ION: + vchargeSetting = round((target_v - 3.8125) * 80.0); + break; + default: + WTF; + break; + } + vchargeSetting = MAX(0, vchargeSetting); + return LTC4015_reg_write(dev, LTC4015_VCHARGE_SETTING_SUBADDR, + vchargeSetting); +} + +ReturnStatus LTC4015_get_cfg_vcharge(LTC4015_Dev *dev, + uint16_t *charge_voltageLevel) // millivolts +{ + /* See datasheet, page 61:VCHARGE_SETTING */ + uint16_t vchargeSetting = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, + LTC4015_VCHARGE_SETTING_SUBADDR, + &vchargeSetting); + switch (dev->cfg.chem) { + case LTC4015_CHEM_LEAD_ACID: + *charge_voltageLevel = + round(((vchargeSetting / 105.0) + 2.0) * + dev->cfg.cellcount * 1000.0); + break; + case LTC4015_CHEM_LI_FE_PO4: + *charge_voltageLevel = + round(((vchargeSetting / 80.0) + 3.4125) * + dev->cfg.cellcount * 1000.0); + break; + case LTC4015_CHEM_LI_ION: + *charge_voltageLevel = + round(((vchargeSetting / 80.0) + 3.8125) * + dev->cfg.cellcount * 1000.0); + break; + default: + WTF; + break; + } + /* TODO: bounds check? */ + + return status; +} + +/* Convert a voltage to a valid vbat register value */ +static uint16_t voltage_to_vbat_reg(LTC4015_Dev *dev, + int16_t voltage) +{ + switch (dev->cfg.chem) { + case LTC4015_CHEM_LEAD_ACID: + return (voltage / (dev->cfg.cellcount * 128.176)) * 1000.0; + case LTC4015_CHEM_LI_FE_PO4: + case LTC4015_CHEM_LI_ION: + return (voltage / (dev->cfg.cellcount * 192.264)) * 1000.0; + default: + WTF; + break; + } + return 0; /* Should never get here, but keeps compiler happy */ +} + +ReturnStatus LTC4015_cfg_battery_voltage_low(LTC4015_Dev *dev, + int16_t underVoltage) //millivolts +{ + /* See datasheet, page 56:VBAT_LO_ALERT_LIMIT + under voltage limit = [VBAT_*_ALERT_LIMIT] • x(uV) */ + return LTC4015_reg_write(dev, LTC4015_VBAT_LO_ALERT_LIMIT_SUBADDR, + voltage_to_vbat_reg(dev, underVoltage)); +} + +/* Convert a voltage to a valid vbat register value */ +static int16_t vbat_reg_to_voltage(LTC4015_Dev *dev, + uint16_t vbat_reg) +{ + switch (dev->cfg.chem) { + case LTC4015_CHEM_LEAD_ACID: + return ((int16_t) vbat_reg / 1000.0) * (128.176 * dev->cfg.cellcount); + case LTC4015_CHEM_LI_FE_PO4: + case LTC4015_CHEM_LI_ION: + return ((int16_t) vbat_reg / 1000.0) * (192.264 * dev->cfg.cellcount); + default: + WTF; + break; + } + return 0; /* Should never get here, but keeps compiler happy */ +} + +ReturnStatus LTC4015_get_cfg_battery_voltage_low(LTC4015_Dev *dev, + int16_t *underVolatage) //millivolts +{ + /* See datasheet, page 56 */ + uint16_t vbatLoLimit = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, + LTC4015_VBAT_LO_ALERT_LIMIT_SUBADDR, + &vbatLoLimit); + *underVolatage = vbat_reg_to_voltage(dev, vbatLoLimit); + return status; +} + +ReturnStatus LTC4015_cfg_battery_voltage_high(LTC4015_Dev *dev, + int16_t overVoltage) //millivolts +{ + /* See datasheet, page 56:VBAT_HI_ALERT_LIMIT + under voltage limit = [VBAT_*_ALERT_LIMIT] • x(uV) */ + return LTC4015_reg_write(dev, LTC4015_VBAT_HI_ALERT_LIMIT_SUBADDR, + voltage_to_vbat_reg(dev, overVoltage)); +} + +ReturnStatus LTC4015_get_cfg_battery_voltage_high(LTC4015_Dev *dev, + int16_t *overVoltage) //millivolts +{ + /* See datasheet, page 56 */ + uint16_t vbatHiLimit = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, + LTC4015_VBAT_HI_ALERT_LIMIT_SUBADDR, + &vbatHiLimit); + *overVoltage = vbat_reg_to_voltage(dev, vbatHiLimit); + return status; +} + + +ReturnStatus LTC4015_cfg_input_voltage_low(LTC4015_Dev *dev, + int16_t inputUnderVoltage) // millivolts +{ + /* See datasheet, page 56:VIN_LO_ALERT_LIMIT + VIN_LO_ALERT_LIMIT = limit/1.648mV */ + uint16_t vinLoLimit = (inputUnderVoltage / (1.648)); + return LTC4015_reg_write(dev, LTC4015_VIN_LO_ALERT_LIMIT_SUBADDR, + vinLoLimit); +} + +ReturnStatus LTC4015_get_cfg_input_voltage_low(LTC4015_Dev *dev, + int16_t *inpUnderVoltage) //millivolts +{ + /* See datasheet, page 56 + * VIN_LO_ALERT_LIMIT = (inpUnderVoltage/(1.648)) */ + uint16_t vInLoAlertLimit = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, + LTC4015_VIN_LO_ALERT_LIMIT_SUBADDR, + &vInLoAlertLimit); + *inpUnderVoltage = (int16_t) vInLoAlertLimit * 1.648; + return status; +} + +ReturnStatus LTC4015_cfg_input_current_high(LTC4015_Dev *dev, + int16_t inputOvercurrent) // milliAmps +{ + /* See datasheet, page 56:IIN_HI_ALERT_LIMIT + IIN_HI_ALERT_LIMIT = (limit*RSNSI)/1.46487uV */ + uint16_t iInHiLimit = ((inputOvercurrent * dev->cfg.r_snsi) / 1.46487); + return LTC4015_reg_write(dev, LTC4015_IIN_HI_ALERT_LIMIT_SUBADDR, + iInHiLimit); +} + +ReturnStatus LTC4015_get_cfg_input_current_high(LTC4015_Dev *dev, + int16_t *inpOverCurrent) +{ + /* See datasheet, page 56 + * IIN_HI_ALERT_LIMIT = ((inpOverCurrent*PWR_INT_BATT_RSNSI)/(1.46487)) */ + uint16_t iInHiALertLimit = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, + LTC4015_IIN_HI_ALERT_LIMIT_SUBADDR, + &iInHiALertLimit); + *inpOverCurrent = ((int16_t) iInHiALertLimit * 1.46487) / dev->cfg.r_snsi; + return status; +} + +ReturnStatus LTC4015_cfg_battery_current_low(LTC4015_Dev *dev, + int16_t lowbattCurrent) +{ + /* See datasheet, page 56:IBAT_LO_ALERT_LIMIT + IBAT_LO_ALERT_LIMIT = (limit*RSNSB)/1.46487uV */ + uint16_t iBatLoAlertLimit = (lowbattCurrent * dev->cfg.r_snsb) / (1.46487); + return LTC4015_reg_write(dev, LTC4015_IBAT_LO_ALERT_LIMIT_SUBADDR, + iBatLoAlertLimit); +} + +ReturnStatus LTC4015_get_cfg_battery_current_low(LTC4015_Dev *dev, + int16_t *lowbattCurrent) +{ + /* See datasheet, page 56 + * IBAT_LO_ALERT_LIMIT = ((current*PWR_INT_BATT_RSNSB)/(1.46487)) */ + uint16_t iBatLoAlertLimit = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, + LTC4015_IBAT_LO_ALERT_LIMIT_SUBADDR, + &iBatLoAlertLimit); + *lowbattCurrent = ((int16_t) iBatLoAlertLimit * 1.46487) / dev->cfg.r_snsb; + return status; +} + +ReturnStatus LTC4015_cfg_die_temperature_high(LTC4015_Dev *dev, + int16_t dieTemp) // Degrees C +{ + /* See datasheet, page 57:DIE_TEMP_HI_ALERT_LIMIT + DIE_TEMP_HI_ALERT_LIMIT = (DIE_TEMP • 12010)/45.6°C */ + uint16_t dieTempAlertLimit = (dieTemp * 45.6) + 12010; + return LTC4015_reg_write(dev, LTC4015_DIE_TEMP_HI_ALERT_LIMIT_SUBADDR, + dieTempAlertLimit); +} + +ReturnStatus LTC4015_get_cfg_die_temperature_high(LTC4015_Dev *dev, + int16_t *dieTemp) // Degrees C +{ + /* See datasheet, page 57 + * DIE_TEMP_HI_ALERT_LIMIT = (dieTemp • 12010)/45.6°C */ + uint16_t dieTempAlertLimit = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, + LTC4015_DIE_TEMP_HI_ALERT_LIMIT_SUBADDR, + &dieTempAlertLimit); + *dieTemp = (((int16_t) dieTempAlertLimit - 12010) / 45.6); + return status; +} + +ReturnStatus LTC4015_cfg_input_current_limit(LTC4015_Dev *dev, + uint16_t inputCurrentLimit) // milliAmps +{ + /* See datasheet, page 61:IIN_LIMIT_SETTING + IIN_LIMIT_SETTING = (limit * RSNSI / 500uV) - 1 */ + /* TODO: range check? this is only a 6-bit register */ + uint16_t iInLimitSetting = ((inputCurrentLimit * dev->cfg.r_snsi) / 500) - 1; + return LTC4015_reg_write(dev, LTC4015_IIN_LIMIT_SETTING_SUBADDR, + iInLimitSetting); +} + +ReturnStatus LTC4015_get_cfg_input_current_limit(LTC4015_Dev *dev, + uint16_t *currentLimit) //milli Amps +{ + /* See datasheet, page 56 + * Input current limit setting = (IIN_LIMIT_SETTING + 1) • 500uV / RSNSI */ + uint16_t iInlimitSetting = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, + LTC4015_IIN_LIMIT_SETTING_SUBADDR, + &iInlimitSetting); + *currentLimit = ((iInlimitSetting + 1) * 500.0) / dev->cfg.r_snsi; + return status; +} + +ReturnStatus LTC4015_get_die_temperature(LTC4015_Dev *dev, + int16_t *dieTemp) // Degrees C +{ + /* Datasheet page 71: temperature = (DIE_TEMP • 12010)/45.6°C */ + uint16_t dieTemperature = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, LTC4015_DIE_TEMP_SUBADDR, + &dieTemperature); + *dieTemp = (((int16_t) dieTemperature - 12010) / 45.6); + return status; +} + +ReturnStatus LTC4015_get_battery_current(LTC4015_Dev *dev, + int16_t *iBatt) //milliAmps +{ + /* Page 70: Battery current = [IBAT] * 1.46487uV/Rsnsb */ + uint16_t batteryCurrent = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, LTC4015_IBAT_SUBADDR, + &batteryCurrent); + *iBatt = ((float) ((int16_t) batteryCurrent * 1.46487)) / (dev->cfg.r_snsb); + return status; +} + +ReturnStatus LTC4015_get_input_current(LTC4015_Dev *dev, + int16_t *iIn) //milliAmps +{ + /* Page 71: Input current = [IIN] • 1.46487uV/Rsnsi */ + uint16_t inputCurrent = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, LTC4015_IIN_SUBADDR, + &inputCurrent); + *iIn = ((float) ((int16_t) inputCurrent * 1.46487)) / (dev->cfg.r_snsi); + return status; +} + +ReturnStatus LTC4015_get_battery_voltage(LTC4015_Dev *dev, + int16_t *vbat) //milliVolts +{ + /* Page 71: 2's compliment VBATSENS/cellcount = [VBAT] • [x]uV */ + uint16_t batteryVoltage = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, LTC4015_VBAT_SUBADDR, + &batteryVoltage); + *vbat = vbat_reg_to_voltage(dev, batteryVoltage); + return status; +} + +ReturnStatus LTC4015_get_input_voltage(LTC4015_Dev *dev, + int16_t *vIn) //milliVolts +{ + /* Page 71: 2's compliment Input voltage = [VIN] • 1.648mV */ + uint16_t inputVoltage = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, LTC4015_VIN_SUBADDR, + &inputVoltage); + *vIn = (int16_t) inputVoltage * 1.648; + return status; +} + +ReturnStatus LTC4015_get_system_voltage(LTC4015_Dev *dev, + int16_t *vSys) //milliVolts +{ + /* Page 71: 2's compliment system voltage = [VSYS] • 1.648mV */ + uint16_t sysVoltage = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, LTC4015_VSYS_SUBADDR, + &sysVoltage); + *vSys = (int16_t) sysVoltage * 1.648; + return status; +} + +ReturnStatus LTC4015_get_icharge_dac(LTC4015_Dev *dev, + int16_t *icharge) //milliAmps +{ + /* Page 72: (ICHARGE_DAC + 1) • 1mV/RSNSB */ + uint16_t ichargeDAC = 0x0000; + ReturnStatus status = LTC4015_reg_read(dev, LTC4015_ICHARGE_DAC_SUBADDR, + &ichargeDAC); + *icharge = (int16_t)((ichargeDAC + 1) / dev->cfg.r_snsb); + return status; +} + +static ReturnStatus _enable_limit_alerts(LTC4015_Dev *dev, uint16_t alertConfig) +{ + return LTC4015_reg_write(dev, LTC4015_EN_LIMIT_ALERTS_SUBADDR, alertConfig); +} + +static ReturnStatus _read_enable_limit_alerts(LTC4015_Dev *dev, uint16_t* regValue) +{ + return LTC4015_reg_read(dev, LTC4015_EN_LIMIT_ALERTS_SUBADDR, regValue); +} + +static ReturnStatus _read_limit_alerts(LTC4015_Dev *dev, uint16_t* regValue) +{ + return LTC4015_reg_read(dev, LTC4015_LIMIT_ALERTS_SUBADDR, regValue); +} + +static ReturnStatus _enable_charger_state_alerts(LTC4015_Dev *dev, + uint16_t regValue) +{ + return LTC4015_reg_write(dev, LTC4015_EN_CHARGER_STATE_ALERTS_SUBADDR, + regValue); +} + +static ReturnStatus _read_enable_charger_state_alerts(LTC4015_Dev *dev, + uint16_t* regValue) +{ + return LTC4015_reg_read(dev, LTC4015_EN_CHARGER_STATE_ALERTS_SUBADDR, + regValue); +} + +static ReturnStatus _read_charger_state_alerts(LTC4015_Dev *dev, + uint16_t *regValue) +{ + return LTC4015_reg_read(dev, LTC4015_CHARGER_STATE_ALERTS_SUBADDR, + regValue); +} + +static ReturnStatus _read_system_status(LTC4015_Dev *dev, uint16_t *regValue) +{ + ReturnStatus status = LTC4015_reg_read(dev, LTC4015_SYSTEM_STATUS_SUBADDR, + regValue); + return status; +} + +ReturnStatus LTC4015_get_bat_presence(LTC4015_Dev *dev, bool *present) +{ + ReturnStatus status = RETURN_OK; + uint16_t value = 0; + status = _read_charger_state_alerts(dev, &value); + *present = !(value & LTC4015_EVT_BMFA); + return status; +} + +static void _ltc4015_isr(void *context) { + LTC4015_Dev *dev = context; + ReturnStatus status = RETURN_OK; + uint16_t alert_status = 0; + int16_t val = 0; + bool present; + + /* See if we have a callback assigned to handle alerts */ + if (!dev->obj.alert_cb) { + return; + } + + /* Check battery missing alarm now */ + status = LTC4015_get_bat_presence(dev, &present); + if (status != RETURN_OK) { + return; + } + + /*If battery is missing no need to check other limit alerts*/ + if (!present) { + dev->obj.alert_cb(LTC4015_EVT_BMFA, val, dev->obj.cb_context); + return; + } + + /* Read the alert status register to clear the alert bits if set) */ + if (_read_limit_alerts(dev, &alert_status) != RETURN_OK) { + LOGGER_DEBUG("LTC4015:ERROR:: INT limit alerts read failed\n"); + return; + } + + if (alert_status & LTC4015_EVT_BVL) { + status = LTC4015_get_battery_voltage(dev, &val); + dev->obj.alert_cb(LTC4015_EVT_BVL, val, dev->obj.cb_context); + } + if (alert_status & LTC4015_EVT_BVH) { + status = LTC4015_get_battery_voltage(dev, &val); + dev->obj.alert_cb(LTC4015_EVT_BVH, val, dev->obj.cb_context); + } + if (alert_status & LTC4015_EVT_IVL) { + status = LTC4015_get_input_voltage(dev, &val); + dev->obj.alert_cb(LTC4015_EVT_IVL, val, dev->obj.cb_context); + } + if (alert_status & LTC4015_EVT_ICH) { + status = LTC4015_get_input_current(dev, &val); + dev->obj.alert_cb(LTC4015_EVT_ICH, val, dev->obj.cb_context); + } + if (alert_status & LTC4015_EVT_BCL) { + status = LTC4015_get_battery_current(dev, &val); + dev->obj.alert_cb(LTC4015_EVT_BCL, val, dev->obj.cb_context); + } + if (alert_status & LTC4015_EVT_DTH) { + status = LTC4015_get_die_temperature(dev, &val); + dev->obj.alert_cb(LTC4015_EVT_DTH, val, dev->obj.cb_context); + } +} + +ReturnStatus LTC4015_init(LTC4015_Dev *dev) +{ + dev->obj = (LTC4015_Obj){}; + + /* TODO: Do the pre-configuration here if needed */ + + if (dev->cfg.pin_alert) { + const uint32_t pin_evt_cfg = OCGPIO_CFG_INPUT | OCGPIO_CFG_INT_FALLING; + if (OcGpio_configure(dev->cfg.pin_alert, pin_evt_cfg) < OCGPIO_SUCCESS) { + return RETURN_NOTOK; + } + + /* Use a threaded interrupt to handle IRQ */ + // ThreadedInt_Init(dev->cfg.pin_alert, _ltc4015_isr, (void *)dev); + } + return RETURN_OK; +} + +void LTC4015_setAlertHandler(LTC4015_Dev *dev, LTC4015_CallbackFn alert_cb, + void *cb_context) { + dev->obj.alert_cb = alert_cb; + dev->obj.cb_context = cb_context; +} + +ReturnStatus LTC4015_enableLimitAlerts(LTC4015_Dev *dev, uint16_t alert_mask) +{ + uint16_t alert_reg; + + /* Read the alert status register to clear the alert bits if set) */ + ReturnStatus res = _read_limit_alerts(dev, &alert_reg); + if (res != RETURN_OK) { + return res; + } + + /* Get the previously configured alerts */ + res = _read_enable_limit_alerts(dev, &alert_reg); + if (res != RETURN_OK) { + return res; + } + + alert_reg |= alert_mask; + + return _enable_limit_alerts(dev, alert_reg); +} + +ReturnStatus LTC4015_enableChargerStateAlerts(LTC4015_Dev *dev, + uint16_t alert_mask) +{ + uint16_t alert_reg; + + /* Read the alert status register to clear the alert bits if set) */ + ReturnStatus res = _read_charger_state_alerts(dev, &alert_reg); + if (res != RETURN_OK) { + return res; + } + + /* Get the previously configured alerts */ + res = _read_enable_charger_state_alerts(dev, &alert_reg); + if (res != RETURN_OK) { + return res; + } + + alert_reg |= alert_mask; + + return _enable_charger_state_alerts(dev, alert_reg); +} + +void LTC4015_configure(LTC4015_Dev *dev) +{ + // OcGpio_configure(&dev->cfg.pin_lt4015_i2c_sel, OCGPIO_CFG_OUTPUT); +} + +ePostCode LTC4015_probe(LTC4015_Dev *dev, POSTData *postData) +{ + uint16_t ltcStatusReg = 0; + /* TODO: Check reading bits from System regsiter is enough to conclude + * whether battery is connected or not */ + if (_read_system_status(dev, <cStatusReg) != RETURN_OK) { + return POST_DEV_MISSING; + } + if (!(ltcStatusReg & LTC4015_CHARGER_ENABLED)) { + return POST_DEV_MISSING; + } + post_update_POSTData(postData, dev->cfg.i2c_dev.bus, dev->cfg.i2c_dev.slave_addr,0xFF, 0xFF); + return POST_DEV_FOUND; +} diff --git a/firmware/src/devices/ltc4015_registers.h b/firmware/src/devices/ltc4015_registers.h new file mode 100644 index 0000000000..71e11efeb0 --- /dev/null +++ b/firmware/src/devices/ltc4015_registers.h @@ -0,0 +1,400 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#ifndef CHARGECTRL_LTC4015_H_ +#define CHARGECTRL_LTC4015_H_ + +/***************************************************************************** + * REGISTER DEFINITIONS + *****************************************************************************/ +// Battery voltage low alert limit - BITS[15:0] 0x0000 +#define LTC4015_VBAT_LO_ALERT_LIMIT_SUBADDR 0x01 +// Battery voltage high alert limit - BITS[15:0] 0x0000 +#define LTC4015_VBAT_HI_ALERT_LIMIT_SUBADDR 0x02 +// Input voltage low alert limit - BITS[15:0] 0x0000 +#define LTC4015_VIN_LO_ALERT_LIMIT_SUBADDR 0x03 +// Input voltage high alert limit - BITS[15:0] 0x0000 +#define LTC4015_VIN_HI_ALERT_LIMIT_SUBADDR 0x04 +// Output voltage low alert limit - BITS[15:0] 0x0000 +#define LTC4015_VSYS_LO_ALERT_LIMIT_SUBADDR 0x05 +// Output voltage high alert limit - BITS[15:0] 0x0000 +#define LTC4015_VSYS_HI_ALERT_LIMIT_SUBADDR 0x06 +// Input current high alert limit - BITS[15:0] 0x0000 +#define LTC4015_IIN_HI_ALERT_LIMIT_SUBADDR 0x07 +// Charge current low alert limit - BITS[15:0] 0x0000 +#define LTC4015_IBAT_LO_ALERT_LIMIT_SUBADDR 0x08 +// Die temperature high alert limit, - BITS[15:0] 0x0000 +#define LTC4015_DIE_TEMP_HI_ALERT_LIMIT_SUBADDR 0x09 +// Battery series resistance high alert limit - BITS[15:0] 0x0000 +#define LTC4015_BSR_HI_ALERT_LIMIT_SUBADDR 0x0A +// Thermistor ratio high (cold battery) alert limit - BITS[15:0] 0x0000 +#define LTC4015_NTC_RATIO_HI_ALERT_LIMIT_SUBADDR 0x0B +// Thermistor ratio low (hot battery) alert limit - BITS[15:0] 0x0000 +#define LTC4015_NTC_RATIO_LO_ALERT_LIMIT_SUBADDR 0x0C + +/* Bit fields: + * + * 15 : Enable meas_sys_valid_alert + * 14 : N/A + * 13 : Enable coulomb counter value low alert + * 12 : Enable coulomb counter value high alert + * 11 : Enable battery undervoltage alert + * 10 : Enable battery overvoltage alert + * 9 : Enable input undervoltage alert + * 8 : Enable input overvoltage alert + * 7 : Enable output undervoltage alert + * 6 : Enable output overvoltage alert + * 5 : Enable input overcurrent alert + * 4 : Enable battery current low alert + * 3 : Enable die temperature high alert + * 2 : Enable battery series resistance high alert + * 1 : Enable thermistor ratio high (cold battery) alert + * 0 : Enable thermistor ratio low (hot battery) alert + */ +// Enable limit monitoring and alert notification via SMBALERT - BITS[15:0] 0x0000 +#define LTC4015_EN_LIMIT_ALERTS_SUBADDR 0x0D + +/* Bit fields: + * + * 15:11 : N/A + * 10 : Enable alert for lead-acid equalize charge state + * 9 : Enable alert for absorb charge state + * 8 : Enable alert for charger suspended state + * 7 : Enable alert for precondition charge state + * 6 : Enable alert for constant current constant voltage state + * 5 : Enable alert for thermistor pause state + * 4 : Enable alert for timer termination state + * 3 : Enable alert for C/x termination state + * 2 : Enable max_charge_time_fault alert + * 1 : Enable alert for missing battery fault state + * 0 : Enable alert for shorted battery fault state + */ +// Enable charger state alert notification via SMBALERT - BITS[15:0] 0x0000 +#define LTC4015_EN_CHARGER_STATE_ALERTS_SUBADDR 0x0E + +/* Bit fields: + * + * 15:4 : N/A + * 3 : Enable alert for input undervoltage current limit active + * 2 : Enable alert for input current limit active + * 1 : Enable alert for constant current status + * 0 : Enable alert for constant voltage status + */ +// Enable charge status alert notification via SMBALERT - BITS[15:0] 0x0000 +#define LTC4015_EN_CHARGE_STATUS_ALERTS_SUBADDR 0x0F + +// Coulomb counter QCOUNT low alert limit, same format as QCOUNT (0x13) - BITS[15:0] : 0x0000 +#define LTC4015_QCOUNT_LO_ALERT_LIMIT_SUBADDR 0x10 +// Coulomb counter QCOUNT high alert limit, same format as QCOUNT (0x13) - BITS[15:0] : 0x0000 +#define LTC4015_QCOUNT_HI_ALERT_LIMIT_SUBADDR 0x11 +// Coulomb counter prescale factor - BITS[15:0] : 0x0200 +#define LTC4015_QCOUNT_PRESCALE_FACTOR_SUBADDR 0x12 +// Coulomb counter value - BITS[15:0] : 0x8000 +#define LTC4015_QCOUNT_SUBADDR 0x13 + +/* Bit fields: + * + * 15:9 : N/A + * 8 : Suspend battery charger operation + * 7:6 : N/A + * 5 : Perform a battery series resistance measurement + * 4 : Force measurement system to operate + * 3 : Enable Maximum Power Point Tracking + * 2 : Enable coulomb counter + * 1:0 : N/A + */ +// Configuration Settings - BITS[15:0] : 0x0000 +#define LTC4015_CONFIG_BITS_SUBADDR 0x14 + +// Input current limit setting = (IIN_LIMIT_SETTING + 1) • 500uV / RSNSI - BITS[5:0] : 0x3F +#define LTC4015_IIN_LIMIT_SETTING_SUBADDR 0x15 +// UVCLFB input undervoltage limit = (VIN_UVCL_SETTING + 1) • 4.6875mV - BITS[7:0] : 0xFF +#define LTC4015_VIN_UVCL_SETTING_SUBADDR 0x16 +#define LTC4015_RESERVED_0X17_SUBADDR 0x17 +#define LTC4015_RESERVED_0X18_SUBADDR 0x18 +// Write 0x534D to arm ship mode. Once armed, ship mode cannot be disarmed. +#define LTC4015_ARM_SHIP_MODE_SUBADDR 0x19 +// Maximum charge current target = (ICHARGE_TARGET + 1) • 1mV/RSNSB - BITS[4:0] +#define LTC4015_ICHARGE_TARGET_SUBADDR 0x1A +// Charge voltage target - BITS[5:0] +#define LTC4015_VCHARGE_SETTING_SUBADDR 0x1B +// Two’s complement Low IBAT threshold for C/x termination - BITS[15:0] +#define LTC4015_C_OVER_X_THRESHOLD_SUBADDR 0x1C +// Time in seconds with battery charger in the CV state before timer termination +// occurs (lithium chemistries only) +#define LTC4015_MAX_CV_TIME_SUBADDR 0x1D +// Time in seconds before a max_charge_time fault is declared. Set to zero to +// disable max_charge_time fault +#define LTC4015_MAX_CHARGE_TIME_SUBADDR 0x1E +// Value of NTC_RATIO for transition between JEITA regions 2 and 1 (off) - BITS[15:0] : 0x3F00 +#define LTC4015_JEITA_T1_SUBADDR 0x1F +// Value of NTC_RATIO for transition between JEITA regions 3 and 2 - BITS[15:0] : 0x372A +#define LTC4015_JEITA_T2_SUBADDR 0x20 +// Value of NTC_RATIO for transition between JEITA regions 4 and 3 - BITS[15:0] : 0x1F27 +#define LTC4015_JEITA_T3_SUBADDR 0x21 +// Value of NTC_RATIO for transition between JEITA regions 5 and 4 - BITS[15:0] : 0x1BCC +#define LTC4015_JEITA_T4_SUBADDR 0x22 +// Value of NTC_RATIO for transition between JEITA regions 6 and 5 - BITS[15:0] : 0x18B9 +#define LTC4015_JEITA_T5_SUBADDR 0x23 +// Value of NTC_RATIO for transition between JEITA regions 7 (off) and 6 - BITS[15:0] : 0x136D +#define LTC4015_JEITA_T6_SUBADDR 0x24 + +/* Bit Fields: + * + * 15:10 : N/A + * 9:5 : vcharge_jeita_6 + * 4:0 : vcharge_jeita_5 + */ +// VCHARGE values for JEITA temperature regions 6 and 5 +#define LTC4015_VCHARGE_JEITA_6_5_SUBADDR 0x25 + +/* Bit Fields: + * + * 15 : N/A + * 14:10 : vcharge_jeita_4 + * 9:5 : vcharge_jeita_3 + * 4:0 : vcharge_jeita_4 + */ +// VCHARGE values for JEITA temperature regions 4, 3, and 2 +#define LTC4015_VCHARGE_JEITA_4_3_2_SUBADDR 0x26 + +/* Bit Fields: + * + * 15:10 : N/A + * 9:5 : icharge_jeita_6 + * 4:0 : icharge_jeita_5 + */ +// ICHARGE_TARGET values for JEITA temperature regions 6 and 5 - BITS[15:0] : 0x01EF +#define LTC4015_ICHARGE_JEITA_6_5_SUBADDR 0x27 + +/* Bit Fields: + * + * 15 : N/A + * 14:10 : icharge_jeita_4 + * 9:5 : icharge_jeita_3 + * 4:0 : icharge_jeita_4 + */ +// ICHARGE_TARGET value for JEITA temperature regions 4, 3, and 2 - BITS[15:0] : 0x7FEF +#define LTC4015_ICHARGE_JEITA_4_3_2_SUBADDR 0x28 + +/* Bit Fields: + * + * 15:3 : N/A + * 2 : Enable C/x termination + * 1 : Enable lead acid charge voltage temperature compensation + * 0 : Enable JEITA temperature profile + */ +// Battery charger configuration settings +#define LTC4015_CHARGER_CONFIG_BITS_SUBADDR 0x29 + +// LiFePO4/lead-acid absorb voltage adder, bits 15:6 are reserved +#define LTC4015_VABSORB_DELTA_SUBADDR 0x2A +// Maximum time for LiFePO4/lead-acid absorb charge +#define LTC4015_MAX_ABSORB_TIME_SUBADDR 0x2B +// Lead-acid equalize charge voltage adder, bits 15:6 are reserved - BITS[15:0] : 0x002A +#define LTC4015_VEQUALIZE_DELTA_SUBADDR 0x2C +// Lead-acid equalization time - BITS[15:0] : 0x0E10 +#define LTC4015_EQUALIZE_TIME_SUBADDR 0x2D +// LiFeP04 recharge threshold - BITS[15:0] : 0x4410 +#define LTC4015_LIFEPO4_RECHARGE_THRESHOLD_SUBADDR 0x2E +#define LTC4015_RESERVED_0X2F_SUBADDR 0x2F +// For lithium chemistries, indicates the time (in sec) that the battery has +// been charging +#define LTC4015_MAX_CHARGE_TIMER_SUBADDR 0x30 +// For lithium chemistries, indicates the time (in sec) that the battery has +// been in constant-voltage regulation +#define LTC4015_CV_TIMER_SUBADDR 0x31 +// For LiFePO4 and lead-acid batteries, indicates the time (in sec) that the +// battery has been in absorb phase +#define LTC4015_ABSORB_TIMER_SUBADDR 0x32 +// For lead-acid batteries, indicates the time (in sec) that the battery has +// been in EQUALIZE phase +#define LTC4015_EQUALIZE_TIMER_SUBADDR 0x33 + +/* Bit Fields: + * + * 15:3 : N/A + * 10 : Indicates battery charger is in lead-acid equalization charge state + * 9 : Indicates battery charger is in absorb charge state + * 8 : Indicates battery charger is in charger suspended state + * 7 : Indicates battery charger is in precondition charge state + * 6 : Indicates battery charger is in CC-CV state + * 5 : Indicates battery charger is in thermistor pause state + * 4 : Indicates battery charger is in timer termination state + * 3 : Indicates battery charger is in C/x termination state + * 2 : indicates battery charger is in max_charge_time_fault state + * 1 : Indicates battery charger is in missing battery fault state + * 0 : Indicates battery charger is in shorted battery fault state + */ +// Real time battery charger state indicator. Individual bits are mutually +// exclusive. Bits 15:11 are reserved. +#define LTC4015_CHARGER_STATE_SUBADDR 0x34 + +/* Bit Fields: + * + * 15:4 : N/A + * 3 : Indicates the input undervoltage control loop is actively + * controlling power delivery based on VIN_UVCL_SETTING + * 2 : Indicates the input current limit control loop is actively + * controlling power delivery based on IIN_LIMIT[_DAC][_SETTING] + * 1 : Indicates the charge current control loop is actively controlling + * power delivery based on ICHARGE_DAC + * 0 : Indicates the battery voltage control loop is actively controlling + * power delivery based on VCHARGE_DAC + */ +// Charge status indicator. Individual bits are mutually exclusive. Only active +// in charging states. +#define LTC4015_CHARGE_STATUS_SUBADDR 0x35 + +/* Bit Fields: + * + * 15 : Indicates that measurement system results have become valid. + * 14 : N/A + * 13 : Indicates QCOUNT has fallen below QCOUNT_LO_ALERT_LIMIT + * 12 : Indicates QCOUNT has exceeded QCOUNT_HI_ALERT_LIMIT + * 11 : Indicates VBAT has fallen below VBAT_LO_ALERT_LIMIT + * 10 : Indicates VBAT has exceeded VBAT_HI_ALERT_LIMIT + * 9 : Indicates VIN has fallen below VIN_LO_ALERT_LIMIT + * 8 : Indicates VIN has exceeded VIN_HI_ALERT_LIMIT + * 7 : Indicates VSYS has fallen below VSYS_LO_ALERT_LIMIT + * 6 : Indicates VSYS has exceeded VSYS_HI_ALERT_LIMIT + * 5 : Indicates IIN has exceeded IIN_HI_ALERT_LIMIT + * 4 : Indicates IBAT has fallen below IBAT_LO_ALERT_LIMIT + * 3 : Indicates DIE_TEMP has exceeded DIE_TEMP_HI_ALERT_LIMIT + * 2 : Indicates BSR has exceeded BSR_HI_ALERT_LIMIT + * 1 : Indicates NTC_RATIO has exceeded NTC_RATIO_HI_ALERT_LIMIT + * 0 : Indicates NTC_RATIO has fallen below NTC_RATIO_LO_ALERT_LIMIT + */ +// Limit alert register.Individual bits are enabled by EN_LIMIT_ALERTS (0x0D). +// Writing 0 to any bit clears that alert. Once set, alert bits remain high +// until cleared or disabled. +#define LTC4015_LIMIT_ALERTS_SUBADDR 0x36 + +/* Bit Fields: + * + * 15:11 : N/A + * 10 : Alert indicates charger has entered equalize charge state + * 9 : Alert indicates charger has entered absorb charge state + * 8 : Alert indicates charger has been suspended + * 7 : Alert indicates charger has entered preconditioning charge state + * 6 : Alert indicates charger has entered CC-CV charge state + * 5 : Alert indicates charger has entered thermistor pause state + * 4 : Alert indicates timer termination has occurred + * 3 : Alert indicates C/x termination has occurred + * 2 : Alert indicates charger has entered max_charge_time_fault state + * 1 : Alert indicates battery missing fault has occurred + * 0 : Alert indicates battery short fault has occurred + */ +// Charger state alert register. Individual bits are enabled by EN_CHARGER_STATE_ALERTS (0x0E). +#define LTC4015_CHARGER_STATE_ALERTS_SUBADDR 0x37 + +/* Bit Fields: + * + * 15:4 : N/A + * 3 : Alert indicates that vin_uvcl_active has occurred + * 2 : Alert indicates iin_limit_active has occurred + * 1 : Alert indicates constant_current has occurred + * 0 : Alert indicates constant_voltage has occurred + */ +// Alerts that CHARGE_STATUS indicators have occurred. +// Individual bits are enabled by EN_CHARGE_STATUS_ALERTS (0x0F) +#define LTC4015_CHARGE_STATUS_ALERTS_SUBADDR 0x38 + +/* Bit Fields: + * + * 15:14 : N/A + * 13 : Indicates that the battery charger is active + * 12 : N/A + * 11 : Indicates the MPPT pin is set to enable Maximum Power Point Tracking + * 10 : Indicates a rising edge has been detected at the EQ pin, and an + * equalize charge is queued + * 9 : Indicates DRVCC voltage is above switching regulator undervoltage + * lockout level (4.3V typ) + * 8 : Indicates an invalid combination of CELLS pin settings + * 7 : N/A + * 6 : Indicates all system conditions are met to allow battery charger + * operation. + * 5 : Indicates no resistor has been detected at the RT pin + * 4 : Indicates die temperature is greater than thermal shutdown level + * (160C typical) + * 3 : Indicates VIN voltage is greater than overvoltage lockout level + * (38.6V typical) + * 2 : Indicates VIN voltage is sufficiently greater than BATSENSE for + * switching regulator operation (200mV typical) + * 1 : Indicates INTVCC voltage is above switching regulator undervoltage + * lockout level (4.3V typ) + * 0 : Indicates INTVCC voltage is greater than measurement system lockout + * level (2.8V typical) + */ +// Real time system status indicator bits +#define LTC4015_SYSTEM_STATUS_SUBADDR 0x39 + +// Two’s complement ADC measurement result for the BATSENS pin. +// VBATSENS/cellcount = [VBAT] • 192.264uV for lithium chemistries. +// VBATSENS/cellcount = [VBAT] • 128.176uV for lead-acid. +#define LTC4015_VBAT_SUBADDR 0x3A + +// Two’s complement ADC measurement result for VIN. +// VVIN = [VIN] • 1.648mV +#define LTC4015_VIN_SUBADDR 0x3B + +// Two’s complement ADC measurement result for VSYS. +// VSYS = [VSYS] • 1.648mV +#define LTC4015_VSYS_SUBADDR 0x3C + +// Two’s complement ADC measurement result for (VCSP – VCSN). +// Charge current (into the battery) is represented as a positive number. +// Battery current = [IBAT] • 1.46487uV/RSNSB +#define LTC4015_IBAT_SUBADDR 0x3D + +// Two’s complement ADC measurement result for (VCLP – VCLN). +// Input current = [IIN] • 1.46487uV/RSNSI +#define LTC4015_IIN_SUBADDR 0x3E + +// Two’s complement ADC measurement result for die temperature. +// Temperature = (DIE_TEMP – 12010)/45.6°C +#define LTC4015_DIE_TEMP_SUBADDR 0x3F + +// Two’s complement ADC measurement result for NTC thermistor ratio. +// RNTC = NTC_RATIO • RNTCBIAS/(21845.0 – NTC_RATIO) +#define LTC4015_NTC_RATIO_SUBADDR 0x40 + +// Calculated battery series resistance. +// For lithium chemistries, series resistance/cellcount = BSR • RSNSB/500.0 +// For lead-acid chemistries, series resistance/cellcount = BSR • RSNSB/750.0 +#define LTC4015_BSR_SUBADDR 0x41 + +// JEITA temperature region of the NTC thermistor (Li Only). +// Active only when EN_JEITA=1 (Only Bits[2:0] used) +#define LTC4015_JEITA_REGION_SUBADDR 0x42 + +/* Bit Fields: + * + * 15:12 : N/A + * 11:8 : programmed battery chemistry + * 7:4 : Reserved + * 3:0 : Cell count as set by CELLS pins + */ +// Readout of CHEM and CELLS pin settings +#define LTC4015_CHEM_CELLS_SUBADDR 0x43 +// Charge current control DAC control bits (Only Bits[4:0] used) +#define LTC4015_ICHARGE_DAC_SUBADDR 0x44 +// Charge voltage control DAC control bits (Only Bits[5:0] used) +#define LTC4015_VCHARGE_DAC_SUBADDR 0x45 +// Input current limit control DAC control word (Only Bits[5:0] used) +#define LTC4015_IIN_LIMIT_DAC_SUBADDR 0x46 +// Digitally filtered two’s complement ADC measurement result for battery voltage +#define LTC4015_VBAT_FILT_SUBADDR 0x47 +// This 16-bit two's complement word is the value of IBAT (0x3D) used in calculating BSR. +#define LTC4015_ICHARGE_BSR_SUBADDR 0x48 +#define LTC4015_RESERVED_0X49_SUBADDR 0x49 +// Measurement valid bit, bit 0 is a 1 when the telemetry(ADC) system is ready +#define LTC4015_MEAS_SYS_VALID_SUBADDR 0x4A + +#endif /* CHARGECTRL_LTC4015_H_ */ diff --git a/firmware/src/devices/ltc4274.c b/firmware/src/devices/ltc4274.c new file mode 100644 index 0000000000..981270e94d --- /dev/null +++ b/firmware/src/devices/ltc4274.c @@ -0,0 +1,1075 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** +#include "Board.h" +#include "inc/common/global_header.h" +#include "inc/common/i2cbus.h" +#include "inc/devices/ltc4274.h" +#include "inc/subsystem/power/power.h" + +#include +#include + +/***************************************************************************** + * MACRO DEFINITIONS + *****************************************************************************/ + + +/* Register map */ +#define LTC4274_REG_INTERRUPT_STATUS 0x00 +#define LTC4274_REG_INTERRUPT_MASK 0x01 +#define LTC4274_REG_POWER_EVENT 0x02 +#define LTC4274_REG_POWER_EVENT_COR 0x03 +#define LTC4274_REG_DETECT_EVENT 0x04 +#define LTC4274_REG_DETECT_EVENT_COR 0x05 +#define LTC4274_REG_FAULT_EVENT 0x06 +#define LTC4274_REG_FAULT_EVENT_COR 0x07 +#define LTC4274_REG_START_EVENT 0x08 +#define LTC4274_REG_START_EVENT_COR 0x09 +#define LTC4274_REG_SUPPLY_EVENT 0x0A +#define LTC4274_REG_SUPPLY_EVENT_COR 0x0B +#define LTC4274_REG_STATUS 0x0C +#define LTC4274_REG_POWER_STATUS 0x10 +#define LTC4274_REG_PNI_STATUS 0x11 +#define LTC4274_REG_OPERATION_MODE 0x12 +#define LTC4274_REG_ENABLE_DUSCONNECT SENSING 0X13 +#define LTC4274_REG_DETECT_CLASS_ENABLE 0x14 +#define LTC4274_REG_MIDSPAN 0x15 +#define LTC4274_REG_MCONF 0x17 +#define LTC4274_REG_DETPB 0x18 +#define LTC4274_REG_PWRPB 0x19 +#define LTC4274_REG_RSTPB 0x1A +#define LTC4274_REG_ID 0x1B +#define LTC4274_REG_TLIMIT 0x1E +#define LTC4274_REG_IP1LSB 0x30 +#define LTC4274_REG_IP1MSB 0x31 +#define LTC4274_REG_VP1LSB 0x32 +#define LTC4274_REG_VP1MSB 0x33 +#define LTC4274_REG_FIRMWARE 0x41 +#define LTC4274_REG_WDOG 0x42 +#define LTC4274_REG_DEVID 0x43 +#define LTC4274_REG_HP_ENABLE 0x44 +#define LTC4274_REG_HP_MODE 0x46 +#define LTC4274_REG_CUT1 0x47 +#define LTC4274_REG_LIM1 0x48 +#define LTC4274_REG_IHP_STATUS 0x49 + +/* Interrupt bits */ +#define LTC4274_SUPPLY_INTERRUPT 0x80 +#define LTC4274_TSTART_INTERRUPT 0x40 +#define LTC4274_TCUT_INTERRUPT 0x20 +#define LTC4274_CLASSIFICATION_INTERRUPT 0x10 +#define LTC4274_DETECT_INTERRUPT 0x08 +#define LTC4274_DISCONNECT_INTERRUPT 0x04 +#define LTC4274_POWERGOOD_INTERRUPT 0x02 +#define LTC4274_PWERENABLE_INTERRUPT 0x01 + +/* Events and status control bits*/ +#define LTC4274_PWR_GOOD_BIT 0x10 +#define LTC4274_PWR_STATE_CHANGE_BIT 0x01 +#define LTC4274_CLASSIFICATION_COMPLETE_BIT 0x10 +#define LTC4274_DETECT_COMPLETE_BIT 0X01 +#define LTC4274_DISCONNECT_TDIS_BIT 0x10 +#define LTC4274_OVERCURRENT_TCUT_BIT 0x01 +#define LTC4274_CURRENT_LIMIT_TOUT_BIT 0x10 +#define LTC4274_START_OCURRENT_TOUT_BIT 0X01 +#define LTC4274_PWR_GOOD_BIT 0x10 +#define LTC4274_PWR_ENABLE_BIT 0x01 +#define LTC4274_LTPOE_BIT 0x80 +#define LTC4274_CLASS_BIT 0x70 +#define LTC4274_DETECT_BIT 0X07 +#define LTC4274_ENABLE_CLASSIFICATION_BIT 0X10 +#define LTC4274_ENABLE_DETECTION_BIT 0x01 +#define LTC4274_SUPPLY_EVENT_FAILURE 0xF0 + +/* PSE Configuration */ +#define LTC4274_INTERRUPT_SET 0x1E +#define LTC4274_OPERATING_MODE_SET 0x03 +#define LTC4274_DETCET_CLASS_ENABLE 0x11 +#define LTC4274_MISC_CONF 0xD1 + +/* PSE operating modes */ +#define LTC4274_SHUTDOWN_MODE 0x00 +#define LTC4274_MANUAL_MODE 0x01 +#define LTC4274_SEMIAUTO_MODE 0x02 +#define LTC4274_AUTO_MODE 0x03 + +#define LTC4274_INTERRUPT_ENABLE 0x80 +#define LTC4274_DETECT_ENABLE 0x40 +#define LTC4274_FAST_IV 0x20 +#define LTC4274_MSD_MASK 0x01 + +#define LTC4274_HP_ENABLE 0x11 + +typedef struct { + ePSEDetection detectStatus; + ePSEClassType classStatus; + ePSEPowerState powerGoodStatus; +} tPower_PSEStatus_Data; + +typedef struct { + tPower_PSEStatus_Data pseStatus; + ePSEAlert psealert; +} tPower_PSEStatus_Info; + +static tPower_PSEStatus_Info PSEStatus_Info; + +/****************************************************************************** + * @fn ltc4274_raw_write + * + * @brief Write to PSE register + * + * @args I2C device, Slave address, register address and value to be written. + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_write(const I2C_Dev *i2c_dev, + uint8_t regAddress, + uint8_t regValue) +{ + ReturnStatus status = RETURN_NOTOK; + I2C_Handle pseHandle = i2c_get_handle(i2c_dev->bus); + if (!pseHandle) { + LOGGER_ERROR("LTC4274:ERROR:: Failed to get I2C Bus for PSE" + "0x%x on bus 0x%x.\n", i2c_dev->slave_addr, i2c_dev->bus); + } else { + status = i2c_reg_write(pseHandle,i2c_dev->slave_addr, regAddress, + regValue, 1); + } + return status; +} + +/****************************************************************************** + * @fn ltc4274_read + * + * @brief read two bytesfrom PSE register. + * + * @args I2C device, Slave address, register address and value read. + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_read(const I2C_Dev *i2c_dev, + uint8_t regAddress, + uint8_t *regValue) +{ + ReturnStatus status = RETURN_NOTOK; + I2C_Handle pseHandle = i2c_get_handle(i2c_dev->bus); + if (!pseHandle) { + LOGGER_ERROR("LTC4274:ERROR:: Failed to get I2C Bus for PSE" + "0x%x on bus 0x%x.\n", i2c_dev->slave_addr, i2c_dev->bus); + } else { + /* TODO: refactor i2c_reg_read to not require uint16 */ + uint16_t value; + status = i2c_reg_read(pseHandle, i2c_dev->slave_addr, regAddress, + &value, 1); + *regValue = (uint8_t)value; + } + return status; +} + + +/****************************************************************************** + * @fn ltc4274_set_cfg_operation_mode + * + * @brief Configure PSE operational mode. + * + * @args I2c device struct, Address and operatingMode + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_set_cfg_operation_mode(const I2C_Dev *i2c_dev, uint8_t operatingMode) +{ + ReturnStatus status = RETURN_OK; + status = ltc4274_write( i2c_dev, LTC4274_REG_OPERATION_MODE, operatingMode); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: Write failed to the Operation mode register of PSE.\n"); + } + return status; +} + +/****************************************************************************** + * @fn ltc4274_get_operation_mode + * + * @brief read PSE operational mode. + * + * @args I2c device struct, Address and operatingMode + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_get_operation_mode(const I2C_Dev *i2c_dev, uint8_t *operatingMode) +{ + ReturnStatus status = RETURN_OK; + status = ltc4274_read( i2c_dev, LTC4274_REG_OPERATION_MODE, operatingMode); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: Read failed from the Operation mode register of PSE.\n"); + } + return status; +} + +/****************************************************************************** + * @fn ltc4274_set_cfg_detect_enable + * + * @brief Configure PSE detect and classification enable. + * + * @args I2c device struct, Address and detectEnable + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_set_cfg_detect_enable(const I2C_Dev *i2c_dev, uint8_t detectEnable) +{ + ReturnStatus status = RETURN_OK; + + //Enable detect and classfication of PD + status = ltc4274_write( i2c_dev, LTC4274_REG_DETECT_CLASS_ENABLE, + detectEnable); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE detect enable setting failed.\n"); + } + return status; +} + +/****************************************************************************** + * @fn ltc4274_get_detect_enable + * + * @brief Read PSE detect and classification enable configuration. + * + * @args I2c device struct, Address and detectEnable + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_get_detect_enable(const I2C_Dev *i2c_dev, uint8_t *detectVal) +{ + ReturnStatus status = RETURN_OK; + //Enable detect and classfication of PD + uint8_t val = 0; + status = ltc4274_read( i2c_dev, LTC4274_REG_DETECT_CLASS_ENABLE, &val); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE detect enable config read failed.\n"); + } + *detectVal = (val & 0x07); + return status; +} + +/****************************************************************************** + * @fn ltc4274_set_interrupt_mask + * + * @brief Configure Interrupts for PSE device. + * + * @args I2c device struct,Address and intrMask + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_set_interrupt_mask(const I2C_Dev *i2c_dev, uint8_t interruptMask) +{ + ReturnStatus status = RETURN_OK; + /* Enable interrupts for power event,power good, supply related faults or + * over currrent*/ + status = ltc4274_write( i2c_dev, LTC4274_REG_INTERRUPT_MASK, interruptMask); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE interrupt mask setting failed.\n"); + } + return status; +} + +/****************************************************************************** + * @fn ltc4274_get_interrupt_mask + * + * @brief Read Interrupts for PSE device configuration. + * + * @args I2c device struct, Address and intrMask + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_get_interrupt_mask(const I2C_Dev *i2c_dev, uint8_t *intrMask) +{ + ReturnStatus status = RETURN_OK; + /* Enable interrupts for power event,power good, supply related faults or + * over currrent*/ + status = ltc4274_read( i2c_dev, LTC4274_REG_INTERRUPT_MASK, intrMask); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE interrupt mask config read failed.\n"); + } + return status; +} + +/****************************************************************************** + * @fn ltc4274_cfg_interrupt_enable + * + * @brief Configure Interrupts for PSE device. + * + * @args I2c device struct, Address and interruptBit + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_cfg_interrupt_enable(const I2C_Dev *i2c_dev, bool enable) +{ + ReturnStatus status = RETURN_OK; + uint8_t interruptEnable = 0x00; + if(enable) { + interruptEnable = LTC4274_INTERRUPT_ENABLE; + } + /*Enable interrupts from Misc register*/ + status = ltc4274_write( i2c_dev, LTC4274_REG_MCONF, interruptEnable); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE interrupt enable failed.\n"); + } + return status; +} + +/****************************************************************************** + * @fn ltc4274_get_interrupt_enable + * + * @brief Reads Interrupts enable config for PSE device. + * + * @args I2c device struct, Address and interruptBit + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_get_interrupt_enable(const I2C_Dev *i2c_dev, uint8_t *interruptEnable) +{ + ReturnStatus status = RETURN_OK; + /*Enable interrupts from Misc register*/ + status = ltc4274_read( i2c_dev, LTC4274_REG_MCONF, interruptEnable); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE interrupt enable config read failed.\n"); + } + return status; +} + +/****************************************************************************** + * @fn ltc4274_set_cfg_pshp_feature + * + * @brief Configure high power feature for LTEPOE++. + * + * @args I2c device struct ,Address and hpEnable + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_set_cfg_pshp_feature(const I2C_Dev *i2c_dev, uint8_t hpEnable) +{ + ReturnStatus status = RETURN_OK; + /*Enbale high power feature */ + status = ltc4274_write( i2c_dev, LTC4274_REG_HP_ENABLE, hpEnable); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE high power mode setting failed.\n"); + } + return status; +} + +/****************************************************************************** + * @fn ltc4274_get_pshp_feature + * + * @brief read high power feature config for LTEPOE++. + * + * @args I2c device struct, Address and hpEnable + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_get_pshp_feature(const I2C_Dev *i2c_dev, uint8_t *hpEnable) +{ + ReturnStatus status = RETURN_OK; + /*Enbale high power feature */ + status = ltc4274_read( i2c_dev, LTC4274_REG_HP_ENABLE, hpEnable); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE high power mode config read failed.\n"); + } + return status; +} +/****************************************************************************** + * @fn _get_pse_class_enum + * + * @brief get PSE classs. + * + * @args value and pointer to class type + * + * @return None + */ +static void _get_pse_class_enum(uint8_t val , ePSEClassType *pseClass) +{ + switch (val) { + case LTC4274_CLASSTYPE_UNKOWN: + { + *pseClass = LTC4274_CLASSTYPE_UNKOWN; + } + break; + case LTC4274_CLASSTYPE_1: + { + *pseClass = LTC4274_CLASSTYPE_1; + } + break; + case LTC4274_CLASSTYPE_2: + { + *pseClass = LTC4274_CLASSTYPE_2; + } + break; + case LTC4274_CLASSTYPE_3: + { + *pseClass = LTC4274_CLASSTYPE_3; + } + break; + case LTC4274_CLASSTYPE_4: + { + *pseClass = LTC4274_CLASSTYPE_4; + } + break; + case LTC4274_CLASSTYPE_RESERVED: + { + *pseClass = LTC4274_CLASSTYPE_RESERVED; + } + break; + case LTC4274_CLASSTYPE_0: + { + *pseClass = LTC4274_CLASSTYPE_0; + } + break; + case LTC4274_OVERCURRENT: + { + *pseClass = LTC4274_OVERCURRENT; + } + break; + case LTC4274_LTEPOE_TYPE_52_7W: + { + *pseClass = LTC4274_LTEPOE_TYPE_52_7W; + } + break; + case LTC4274_LTEPOE_TYPE_70W: + { + *pseClass = LTC4274_LTEPOE_TYPE_70W; + } + break; + case LTC4274_LTEPOE_TYPE_90W: + { + *pseClass = LTC4274_LTEPOE_TYPE_90W; + } + break; + case LTC4274_LTEPOE_TYPE_38_7W: + { + *pseClass = LTC4274_LTEPOE_TYPE_38_7W; + } + break; + default: + { + *pseClass = LTC4274_LTEPOE_RESERVED; + } + } +} + + +/****************************************************************************** + * @fn _get_pse_detect_enum + * + * @brief get PSE detect. + * + * @args value and pointer to detection status + * + * @return None + */ +static void _get_pse_detect_enum(uint8_t val , ePSEDetection *pseDetect) +{ + switch (val) { + case LTC4274_DETECT_UNKOWN: + { + *pseDetect = LTC4274_DETECT_UNKOWN; + } + break; + case LTC4274_SHORT_CIRCUIT: + { + *pseDetect = LTC4274_SHORT_CIRCUIT; + } + break; + case LTC4274_CPD_HIGH: + { + *pseDetect = LTC4274_CPD_HIGH; + } + break; + case LTC4274_RSIG_LOW: + { + *pseDetect = LTC4274_RSIG_LOW; + } + break; + case LTC4274_SIGNATURE_GOOD: + { + *pseDetect = LTC4274_SIGNATURE_GOOD; + } + break; + case LTC4274_RSIG_TOO_HIGH: + { + *pseDetect = LTC4274_RSIG_TOO_HIGH; + } + case LTC4274_OPEN_CIRCUIT: + { + *pseDetect = LTC4274_OPEN_CIRCUIT; + } + break; + default: + { + *pseDetect = LTC4274_DETECT_ERROR; + } + } +} +/****************************************************************************** + * @fn ltc4274_get_detection_status + * + * @brief Read PSE class + * + * @args I2c device struct and pointer to class + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_get_detection_status(const I2C_Dev *i2c_dev, ePSEDetection *pseDetect) +{ + ReturnStatus status = RETURN_OK; + uint8_t val = 0; + status = ltc4274_read( i2c_dev, LTC4274_REG_DETECT_EVENT, &val); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE detection status read failed.\n"); + } + if (!(LTC4274_DETECTION_COMPLETE(val))) { + *pseDetect =LTC4274_DETECT_ERROR; + } else { + status = ltc4274_read( i2c_dev, LTC4274_REG_STATUS, &val); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE detection code read failed.\n"); + } + val = LTC4374_DETECT(val); + _get_pse_detect_enum(val,pseDetect); + } + + return status; +} + +/****************************************************************************** + * @fn ltc4274_get_class_status + * + * @brief Read PSE class + * + * @args I2c device struct and pointer to class + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_get_class_status(const I2C_Dev *i2c_dev, ePSEClassType *pseClass) +{ + ReturnStatus status = RETURN_OK; + uint8_t val = 0; + status = ltc4274_read( i2c_dev, LTC4274_REG_DETECT_EVENT, &val); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE class status read failed.\n"); + } + if (!(LTC4274_CLASSIFICATION_COMPLETE(val))) { + *pseClass = LTC4274_CLASS_ERROR; + } else { + status = ltc4274_read( i2c_dev, LTC4274_REG_STATUS, &val); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE class code read failed.\n"); + } + val = LTC4374_CLASS(val); + _get_pse_class_enum(val, pseClass); + } + return status; +} + +/****************************************************************************** + * @fn ltc4274_get_powergood_status + * + * @brief Read PSE power good + * + * @args I2c device struct, Address and detectEnable + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_get_powergood_status(const I2C_Dev *i2c_dev, uint8_t *psePwrGood) +{ + ReturnStatus status = RETURN_OK; + status = ltc4274_read( i2c_dev, LTC4274_REG_POWER_STATUS, psePwrGood); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: PSE power good read failed.\n"); + } + if(LTC4274_PWRGD(*psePwrGood)) { + *psePwrGood = LTC4274_POWERGOOD; + } else { + *psePwrGood = LTC4274_POWERGOOD_NOTOK; + } + return status; +} + + + + +/***************************************************************************** + * Internal IRQ handler - reads in triggered interrupts and dispatches CBs + *****************************************************************************/ +static void ltc4274_handle_irq(void *context) { + LTC4274_Dev *dev = context; + uint8_t alertVal; + if (ltc4274_get_interrupt_status(&dev->cfg.i2c_dev, &alertVal) != RETURN_OK) { + /* Something really strange happened */ + return; + } + /* See if we have a callback assigned to handle alerts */ + if (!dev->obj.alert_cb) { + return; + } + switch(alertVal) { + case LTC4274_EVT_SUPPLY: + { + dev->obj.alert_cb(LTC4274_EVT_SUPPLY, dev->obj.cb_context); + } + break; + case LTC4274_EVT_TSTART: + { + dev->obj.alert_cb(LTC4274_EVT_TSTART, dev->obj.cb_context); + } + break; + case LTC4274_EVT_TCUT: + { + dev->obj.alert_cb(LTC4274_EVT_TCUT, dev->obj.cb_context); + } + break; + case LTC4274_EVT_CLASS: + { + dev->obj.alert_cb(LTC4274_EVT_CLASS, dev->obj.cb_context); + } + break; + case LTC4274_EVT_DETECTION: + { + dev->obj.alert_cb(LTC4274_EVT_DETECTION, dev->obj.cb_context); + } + break; + case LTC4274_EVT_DISCONNECT: + { + dev->obj.alert_cb(LTC4274_EVT_DISCONNECT, dev->obj.cb_context); + } + break; + case LTC4274_EVT_POWERGOOD: + { + dev->obj.alert_cb(LTC4274_EVT_POWERGOOD, dev->obj.cb_context); + } + break; + case LTC4274_EVT_POWER_ENABLE: + { + dev->obj.alert_cb(LTC4274_EVT_POWER_ENABLE, dev->obj.cb_context); + } + break; + default: + { + dev->obj.alert_cb(LTC4274_EVT_NONE, dev->obj.cb_context); + } + + } +} +/***************************************************************************** + *****************************************************************************/ +void ltc4274_set_alert_handler(LTC4274_Dev *dev, LTC4274_CallbackFn alert_cb, + void *cb_context) +{ + dev->obj.alert_cb = alert_cb; + dev->obj.cb_context = cb_context; +} + +/****************************************************************************** + * @fn ltc4274_clear_interrupt + * + * @brief Read powerGood info and power event info. + * + * @args A I2c device struct and address + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_clear_interrupt( const I2C_Dev *i2c_dev, + uint8_t *pwrEvent, + uint8_t *overCurrent, + uint8_t *supply) +{ + ReturnStatus status = RETURN_OK; + status = ltc4274_read( i2c_dev, LTC4274_REG_POWER_EVENT_COR, pwrEvent); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: Reading power good for PSE failed.\n"); + } + + /*Bit 4 for power good and bit 0 for power event*/ + LOGGER("PSELTC4274::INFO:: PSE power Good Info and Power ecent info is read with 0x%x.\n", + *pwrEvent); + + /* if it is due to over current*/ + status = ltc4274_read( i2c_dev, LTC4274_REG_START_EVENT_COR, overCurrent); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR::Reading power good for PSE failed.\n"); + } + + LOGGER("PSELTC4274::INFO:: PSE power Good Info and Power ecent info is read with 0x%x.\n", + *overCurrent); + + /* if its due to supply */ + status = ltc4274_read( i2c_dev, LTC4274_REG_SUPPLY_EVENT_COR, supply); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR::Reading power good for PSE failed.\n"); + } + + LOGGER("PSELTC4274::INFO:: PSE power Good Info and Power ecent info is read with 0x%x.\n", + *supply); + + return status; +} + +/****************************************************************************** + * @fn ltc4274_get_interrupt_status + * + * @brief Read active PSE interrupt info. + * + * @args I2c device struct and Address + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_get_interrupt_status(const I2C_Dev *i2c_dev, uint8_t *val) +{ + ReturnStatus status = RETURN_OK; + uint8_t interruptVal = 0; + status = ltc4274_read( i2c_dev, LTC4274_REG_INTERRUPT_STATUS, &interruptVal); + if (status != RETURN_OK) { + LOGGER("PSELTC4274: ERROR:Reading power good for PSE failed.\n"); + + } + LOGGER("PSELTC4274::INFO: PSE interrupt state is 0x%x.\n", interruptVal); + *val = interruptVal; + return status; +} + +/********************************************************************************* + * @fn ltc4274_debug_write + * + * @brief debug PSE device. + * + * @args I2c device struct, Address and value + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_debug_write(const I2C_Dev *i2c_dev, + uint8_t reg_address, uint8_t value) +{ + ReturnStatus status = RETURN_OK; + /*Enbale high power feature */ + status = ltc4274_write( i2c_dev, reg_address, value); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: Debug write command for reg 0x%x failed.\n", + reg_address); + } + return status; +} + +/********************************************************************************* + * @fn ltc4274_debug_read + * + * @brief debug PSE device. + * + * @args I2c device struct, Address and pointer to value + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_debug_read(const I2C_Dev *i2c_dev, + uint8_t reg_address, uint8_t *value) +{ + ReturnStatus status = RETURN_OK; + /*Enbale high power feature */ + status = ltc4274_read( i2c_dev, reg_address, value); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR:: Debug read command for reg 0x%x failed.\n", + reg_address); + } + return status; +} + + +/****************************************************************************** + * @fn ltc4274_enable + * + * @brief Enable . + *WR + * @args On/off + * + * @return void + */ +void ltc4274_enable(LTC4274_Dev* dev, uint8_t enableVal) +{ + if (enableVal) { + OcGpio_write(&dev->cfg.reset_pin, false); + } else { + OcGpio_write(&dev->cfg.reset_pin, true); + } +} + +/****************************************************************************** + * @fn ltc4274_get_devid + * + * @brief Read PSE device id. + * + * @args I2c device struct and Pointer to device Id. + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_get_devid(const I2C_Dev *i2c_dev, + uint8_t *devID) +{ + ReturnStatus ret = RETURN_OK; + ret = ltc4274_read(i2c_dev,LTC4274_REG_ID,devID); + if (ret != RETURN_OK) { + LOGGER("LTC4274:ERROR:: Reading Device Id for PSE failed.\n"); + } + *devID = LTC4274_DEVID(*devID); + return ret; +} + +/********************************************************************************* + * @fn ltc4274_detect + * + * @brief Reads the detectioin and classification register. + * + * @args I2c device struct and Addrress + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_detect(const I2C_Dev *i2c_dev, + uint8_t *detect, uint8_t *val) +{ + ReturnStatus status = RETURN_OK; + *val = 0; + status = ltc4274_read( i2c_dev, LTC4274_REG_DETECT_EVENT, detect); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR::Reading PSE port detection failed.\n"); + return status; + } + LOGGER("PSELTC4274::Reading PSE port detection done.\n"); + + status = ltc4274_read( i2c_dev, LTC4274_REG_STATUS, val); + if (status != RETURN_OK) { + LOGGER("LTC4274:ERROR::Reading PSE port classificatin failed.\n"); + + } + LOGGER("PSELTC4274::Reading PSE port classification is 0x%x.\n", *val); + + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ltc4274_config + ** + ** DESCRIPTION : configure gpio and bring it out of reset . + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +void ltc4274_config(LTC4274_Dev *dev) +{ + OcGpio_configure(&dev->cfg.reset_pin, OCGPIO_CFG_OUTPUT | + OCGPIO_CFG_OUT_HIGH); + //Enable PSE device. + ltc4274_enable(dev,true); +} + +/***************************************************************************** + ** FUNCTION NAME : ltc4274_probe + ** + ** DESCRIPTION : reset PSE device. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +ePostCode ltc4274_probe(const LTC4274_Dev *dev, POSTData *postData) +{ + ePostCode postcode = POST_DEV_MISSING; + uint8_t devId = 0x00; + ReturnStatus status = RETURN_NOTOK; + status = ltc4274_get_devid(&dev->cfg.i2c_dev, &devId); + if (status != RETURN_OK) { + postcode = POST_DEV_MISSING; + } else if (devId == LTC4274_DEV_ID){ + postcode = POST_DEV_FOUND; + } else { + postcode = POST_DEV_ID_MISMATCH; + } + post_update_POSTData(postData, dev->cfg.i2c_dev.bus, dev->cfg.i2c_dev.slave_addr,0xFF, devId); + return postcode; +} + +/***************************************************************************** + ** FUNCTION NAME : ltc4274_reset + ** + ** DESCRIPTION : reset PSE device. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : Success or Failure + ** + *****************************************************************************/ +ReturnStatus ltc4274_reset(LTC4274_Dev *dev) +{ + ReturnStatus status = RETURN_OK; + + + OcGpio_write(&dev->cfg.reset_pin, true); + Task_sleep(100); + OcGpio_write(&dev->cfg.reset_pin, false); + + + return status; +} + +/****************************************************************************** + * @fn ltc4274_default_cfg + * + * @brief configure PSE device. + * + * @args I2c device struct and PSE device configurations. + * + * @return ReturnStatus + */ +ReturnStatus ltc4274_default_cfg( const I2C_Dev *i2c_dev, + uint8_t operatingMode, + uint8_t detectEnable, + uint8_t intrMask, + uint8_t interruptEnable, + uint8_t hpEnable) +{ + ReturnStatus ret = RETURN_OK; + ret = ltc4274_set_cfg_operation_mode(i2c_dev,operatingMode); + if (ret != RETURN_OK) { + LOGGER("LTC4274::ERROR: PSE operational mode setting mode failed.\n"); + return ret; + } + + ret = ltc4274_set_cfg_detect_enable(i2c_dev,detectEnable); + if (ret != RETURN_OK) { + LOGGER("LTC4274::ERROR: PSE detection and classification enable failed.\n"); + return ret; + } + + ret = ltc4274_set_interrupt_mask(i2c_dev,intrMask); + if (ret != RETURN_OK) { + LOGGER("LTC4274::ERROR:PSE interrupts mask enable failed.\n"); + return ret; + } + + ret = ltc4274_cfg_interrupt_enable(i2c_dev,interruptEnable); + if (ret != RETURN_OK) { + LOGGER("LTC4274::ERROR: PSE interrupt enable failed.\n"); + return ret; + } + + ret = ltc4274_set_cfg_pshp_feature(i2c_dev,hpEnable); + if (ret != RETURN_OK) { + LOGGER("LTC4274::ERROR: PSE configured for LTEPOE++.\n"); + return ret; + } + return ret; +} + +/***************************************************************************** + ** FUNCTION NAME : ltc4274_init + ** + ** DESCRIPTION : PSE Status is intialized. + ** + ** ARGUMENTS : None. + ** + ** RETURN TYPE : ReturnStatus + ** + *****************************************************************************/ +void ltc4274_init(LTC4274_Dev *dev) +{ + dev->obj = (LTC4274_Obj){}; + + dev->obj.mutex = GateMutex_create(NULL, NULL); + if (!dev->obj.mutex) { + return; + } + + ltc4274_initPSEStateInfo(); + + if (dev->cfg.pin_evt) { + const uint32_t pin_evt_cfg = OCGPIO_CFG_INPUT | OCGPIO_CFG_INT_FALLING; + if (OcGpio_configure(dev->cfg.pin_evt, pin_evt_cfg) < OCGPIO_SUCCESS) { + return ; + } + + /* Use a threaded interrupt to handle IRQ */ + // ThreadedInt_Init(dev->cfg.pin_evt, ltc4274_handle_irq, (void *)dev); + } +} +/***************************************************************************** + ** FUNCTION NAME : ltc4274_initPSEStateInfo + ** + ** DESCRIPTION : PSE Status is intialized. + ** + ** ARGUMENTS : None. + ** + ** RETURN TYPE : ReturnStatus + ** + *****************************************************************************/ +void ltc4274_initPSEStateInfo() +{ + PSEStatus_Info.pseStatus.detectStatus = LTC4274_DETECT_UNKOWN; + PSEStatus_Info.pseStatus.classStatus = LTC4274_CLASSTYPE_UNKOWN; + PSEStatus_Info.pseStatus.powerGoodStatus = LTC4274_POWERGOOD_NOTOK; + PSEStatus_Info.psealert = LTC4274_DISCONNECT_ALERT; +} + +/***************************************************************************** + ** FUNCTION NAME : ltc4274_update_state + ** + ** DESCRIPTION : PSE Status to update. + ** + ** ARGUMENTS : I2c struct + ** + ** RETURN TYPE : void + ** + *****************************************************************************/ +void ltc4274_update_stateInfo(const I2C_Dev *i2c_dev) +{ + ReturnStatus status = RETURN_OK; + status = ltc4274_get_powergood_status( i2c_dev, + &PSEStatus_Info.pseStatus.powerGoodStatus); + if (status != RETURN_OK) { + LOGGER("PDLTC4275::ERROR: Power good signal read failed.\n"); + PSEStatus_Info.pseStatus.detectStatus = LTC4274_DETECT_UNKOWN; + PSEStatus_Info.pseStatus.classStatus = LTC4274_CLASSTYPE_UNKOWN; + PSEStatus_Info.psealert = LTC4274_DISCONNECT_ALERT; + return; + } + if (PSEStatus_Info.pseStatus.powerGoodStatus == LTC4274_POWERGOOD) { + status = ltc4274_get_detection_status(i2c_dev, &PSEStatus_Info.pseStatus.detectStatus); + if (status != RETURN_OK) { + LOGGER("PDLTC4275::ERROR: Reading PSE detection failed.\n"); + PSEStatus_Info.pseStatus.detectStatus = LTC4274_DETECT_UNKOWN; + PSEStatus_Info.pseStatus.classStatus = LTC4274_CLASSTYPE_UNKOWN; + PSEStatus_Info.psealert = LTC4274_DISCONNECT_ALERT; + return; + } + status = ltc4274_get_class_status(i2c_dev, &PSEStatus_Info.pseStatus.classStatus); + if (status != RETURN_OK) { + LOGGER("PDLTC4275::ERROR: Reading PSE classification failed.\n"); + PSEStatus_Info.pseStatus.detectStatus = LTC4274_DETECT_UNKOWN; + PSEStatus_Info.pseStatus.classStatus = LTC4274_CLASSTYPE_UNKOWN; + PSEStatus_Info.psealert = LTC4274_DISCONNECT_ALERT; + return; + } + status = ltc4274_get_interrupt_status(i2c_dev, &PSEStatus_Info.psealert); + if (status != RETURN_OK) { + LOGGER("PDLTC4275::ERROR: Reading PSE detection failed.\n"); + PSEStatus_Info.pseStatus.detectStatus = LTC4274_DETECT_UNKOWN; + PSEStatus_Info.pseStatus.classStatus = LTC4274_CLASSTYPE_UNKOWN; + PSEStatus_Info.psealert = LTC4274_DISCONNECT_ALERT; + return; + } + } +} diff --git a/firmware/src/devices/ltc4295.c b/firmware/src/devices/ltc4295.c new file mode 100644 index 0000000000..5e7c972358 --- /dev/null +++ b/firmware/src/devices/ltc4295.c @@ -0,0 +1,266 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** +#include "devices/i2c/threaded_int.h" +#include "helpers/math.h" +#include "helpers/memory.h" +#include "inc/common/byteorder.h" +#include "inc/devices/ltc4295.h" +#include "inc/subsystem/power/power.h" + +#include +#include + +tPower_PDStatus_Info PDStatus_Info; + + +/****************************************************************************** + * @fn ltc4295_handle_irq + * + * @brief Read the change in the PD state and callbacks the registerd function. + * + * @args Alert Context + * + * @return None + */ +static void ltc4295_handle_irq(void *context) { + LTC4295_Dev *dev = context; + + const IArg mutexKey = GateMutex_enter(dev->obj.mutex); { + ltc4295_update_status(dev); + } GateMutex_leave(dev->obj.mutex, mutexKey); + + /* See if we have a callback assigned to handle alerts */ + if (!dev->obj.alert_cb) { + return; + } + + /* Since > CRIT implies above window, we only handle the highest priority + * event to avoid duplicate events being sent */ + if (PDStatus_Info.pdalert == LTC4295_CONNECT_ALERT) { + dev->obj.alert_cb(LTC4295_CONNECT_EVT, dev->obj.cb_context); + } else if (PDStatus_Info.pdalert == LTC4295_DISCONNECT_ALERT) { + dev->obj.alert_cb(LTC4295_DISCONNECT_EVT, dev->obj.cb_context); + } else if (PDStatus_Info.pdalert == LTC4295_INCOMPATIBLE_ALERT) { + dev->obj.alert_cb(LTC4295_INCOMPATIBLE_EVT, dev->obj.cb_context); + } +} + +/****************************************************************************** + * @fn ltc4295_configure + * + * @brief configure GPIO's. + * + * @args None + * + * @return None + */ +void ltc4295_config(const LTC4295_Dev *dev) +{ + OcGpio_configure(dev->cfg.pin_evt, OCGPIO_CFG_INPUT); + OcGpio_configure(dev->cfg.pin_detect, OCGPIO_CFG_INPUT); +} + +/****************************************************************************** + * @fn ltc4295_probe + * + * @brief Intializes PD update struct. + * + * @args None + * + * @return None + */ +ePostCode ltc4295_probe(const LTC4295_Dev *dev, POSTData *postData) +{ + ePostCode postCode = POST_DEV_MISSING; + ePDPowerState pdStatus = LTC4295_POWERGOOD_NOTOK; + ReturnStatus ret = ltc4295_get_power_good(dev, &pdStatus); + if (ret != RETURN_OK) { + LOGGER("LTC4295::ERROR: Power good signal read failed.\n"); + return postCode; + } + if (pdStatus == LTC4295_POWERGOOD ) { + PDStatus_Info.pdStatus.classStatus = LTC4295_CLASSTYPE_UNKOWN; + PDStatus_Info.pdStatus.powerGoodStatus = LTC4295_POWERGOOD; + PDStatus_Info.state = LTC4295_STATE_NOTOK; + PDStatus_Info.pdalert = LTC4295_DISCONNECT_ALERT; + postCode = POST_DEV_FOUND; + } else { + PDStatus_Info.pdStatus.classStatus = LTC4295_CLASSTYPE_UNKOWN; + PDStatus_Info.pdStatus.powerGoodStatus = LTC4295_POWERGOOD_NOTOK; + PDStatus_Info.state = LTC4295_STATE_NOTOK; + PDStatus_Info.pdalert = LTC4295_DISCONNECT_ALERT; + } + post_update_POSTData(postData, 0xFF, 0xFF, 0xFF, 0xFF); + return postCode; +} + +/****************************************************************************** + * @fn ltc4295_init + * + * @brief Intializes PD update struct. + * + * @args None + * + * @return None + */ +ReturnStatus ltc4295_init(LTC4295_Dev *dev) +{ + ReturnStatus ret = RETURN_OK; + dev->obj = (LTC4295_Obj){}; + + dev->obj.mutex = GateMutex_create(NULL, NULL); + if (!dev->obj.mutex) { + return RETURN_NOTOK; + } + + ret = ltc4295_get_power_good(dev, &PDStatus_Info.pdStatus.powerGoodStatus); + if (ret != RETURN_OK) { + LOGGER("LTC4295::ERROR: Power good signal read failed.\n"); + return ret; + } + if (PDStatus_Info.pdStatus.powerGoodStatus == LTC4295_POWERGOOD) { + ret = ltc4295_get_class(dev, &PDStatus_Info.pdStatus.classStatus); + if (ret != RETURN_OK) { + LOGGER("LTC4295::ERROR: Reading PD classification failed.\n"); + return ret; + } + if (PDStatus_Info.pdStatus.classStatus == LTC4295_CLASSTYPE_POEPP) { + PDStatus_Info.state = LTC4295_STATE_OK; + } + } + + if (dev->cfg.pin_evt) { + const uint32_t pin_evt_cfg = OCGPIO_CFG_INPUT | OCGPIO_CFG_INT_BOTH_EDGES ; + if (OcGpio_configure(dev->cfg.pin_evt, pin_evt_cfg) < OCGPIO_SUCCESS) { + return RETURN_NOTOK; + } + + /* Use a threaded interrupt to handle IRQ */ + // ThreadedInt_Init(dev->cfg.pin_evt, ltc4295_handle_irq, (void *)dev); + } + return ret; +} + +/****************************************************************************** + * @fn ltc4295_set_alert_handler + * + * @brief Set the alert callback function and context. + * + * @args Device, callBack function and context + * + * @return None + */ +void ltc4295_set_alert_handler(LTC4295_Dev *dev, LTC4295_CallbackFn alert_cb, + void *cb_context) +{ + dev->obj.alert_cb = alert_cb; + dev->obj.cb_context = cb_context; +} + +/****************************************************************************** + * @fn ltc4295_get_power_good + * + * @brief Read GPIO status based on that decide power good signal. + * + * @args Addrress + * + * @return ReturnStatus + */ +ReturnStatus ltc4295_get_power_good(const LTC4295_Dev *dev, ePDPowerState *val) +{ + ReturnStatus ret = RETURN_OK; + /*set default to 1*/ + *val = LTC4295_POWERGOOD_NOTOK; + + /* Check Power Good */ + *val = (ePDPowerState) OcGpio_read(dev->cfg.pin_evt); + if(*val == 0) + { + *val = LTC4295_POWERGOOD; + } + DEBUG("LTC4295:INFO:: PD power good is %d.\n", *val); + return ret; +} + +/****************************************************************************** + * @fn ltc4295_get_class + * + * @brief ReadGPIO status based on that decide the PD class. + * + * @args Addrress + * + * @return ReturnStatus + */ +ReturnStatus ltc4295_get_class(const LTC4295_Dev *dev, ePDClassType *val) +{ + ReturnStatus ret = RETURN_OK; + uint8_t i = 0; + uint8_t value = 1; + uint8_t prev_value = 1; + uint8_t toggle = 0; + + for (i = 0; i < 15; i++) { + value = OcGpio_read(dev->cfg.pin_detect); + LOGGER_DEBUG("LTC4295:INFO:: PD-nT2P activity status %d.\n", value); + if (value == 1) { + *val = LTC4295_CLASSTYPE_2; + } else if (value == 0) { + *val = LTC4295_CLASSTYPE_1; + } + /*Incremented only in the case of POE++ device*/ + if (prev_value != value) { + toggle++; + } + prev_value = value; + Task_sleep(3); + } + if (toggle > 2) { + *val = LTC4295_CLASSTYPE_POEPP; + } + LOGGER("LTC4295:INFO:: PD detects POE of class 0x%x.\n", *val); + return ret; +} + +/****************************************************************************** + * @fn ltc4295_update_status + * + * @brief Maintains the PS status. + * + * @args CLass and power good state of PD. + * + * @return None + */ +void ltc4295_update_status(const LTC4295_Dev *dev) +{ + ReturnStatus ret = RETURN_NOTOK; + ret = ltc4295_get_power_good(dev,&PDStatus_Info.pdStatus.powerGoodStatus); + if (ret != RETURN_OK) { + LOGGER("LTC4295::ERROR: Power good signal read failed.\n"); + return; + } + if (PDStatus_Info.pdStatus.powerGoodStatus == LTC4295_POWERGOOD) { + ret = ltc4295_get_class(dev, &PDStatus_Info.pdStatus.classStatus); + if (ret != RETURN_OK) { + LOGGER("LTC4295::ERROR: Reading PD classification failed.\n"); + return; + } + if (PDStatus_Info.pdStatus.classStatus == LTC4295_CLASSTYPE_POEPP) { + PDStatus_Info.state = LTC4295_STATE_OK; + PDStatus_Info.pdalert = LTC4295_CONNECT_ALERT; + } + } else { + PDStatus_Info.state = LTC4295_STATE_NOTOK; + PDStatus_Info.pdalert = LTC4295_DISCONNECT_ALERT; + PDStatus_Info.pdStatus.classStatus == LTC4295_CLASSTYPE_UNKOWN; + } +} diff --git a/firmware/src/devices/ocmp_wrappers/ocmp_cat24c04.c b/firmware/src/devices/ocmp_wrappers/ocmp_cat24c04.c new file mode 100644 index 0000000000..378494bf32 --- /dev/null +++ b/firmware/src/devices/ocmp_wrappers/ocmp_cat24c04.c @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "common/inc/ocmp_wrappers/ocmp_eeprom_cat24c04.h" + +#include "common/inc/global/Framework.h" +#include "inc/common/global_header.h" +#include "inc/devices/eeprom.h" + +#include + +static ePostCode _init_eeprom(void *driver, const void **config, + const void *alert_token) +{ + Eeprom_Cfg *eeprom = (Eeprom_Cfg *)driver; + uint8_t write = 0x01; + uint8_t read = 0x00; + + eeprom_init(eeprom); + eeprom_enable_write(eeprom); + eeprom_write(eeprom, OC_TEST_ADDRESS, &write, 1); + NOP_DELAY(); /* TODO: the eeprom driver should handle this */ + eeprom_disable_write(eeprom); + eeprom_read(eeprom, OC_TEST_ADDRESS, &read, 1); + + if (write == read) { + return POST_DEV_CFG_DONE; + } + return POST_DEV_CFG_FAIL; +} + +const Driver_fxnTable CAT24C04_psu_sid_fxnTable = { + /* Message handlers */ + .cb_init = _init_eeprom, +}; + +const Driver_fxnTable CAT24C04_psu_inv_fxnTable= { + .cb_init = _init_eeprom, +}; diff --git a/firmware/src/devices/ocmp_wrappers/ocmp_debugi2c.c b/firmware/src/devices/ocmp_wrappers/ocmp_debugi2c.c new file mode 100644 index 0000000000..5c197a3126 --- /dev/null +++ b/firmware/src/devices/ocmp_wrappers/ocmp_debugi2c.c @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#include "common/inc/global/ocmp_frame.h" +#include "common/inc/global/Framework.h" +#include "inc/common/global_header.h" +#include "inc/common/i2cbus.h" +#include "inc/devices/debug_oci2c.h" +#include "inc/ocmp_wrappers/ocmp_debugi2c.h" + +/* TI-RTOS driver files */ +#include + +/***************************************************************************** + ** FUNCTION NAME : i2c_read + ** + ** DESCRIPTION : i2c read + ** + ** ARGUMENTS : i2c bus config, i2c config + ** + ** RETURN TYPE : RETURN_OK or RETURN_NOTOK + ** + *****************************************************************************/ +bool i2c_read(void* i2c_cfg, void *oci2c ) +{ + S_I2C_Cfg* s_oc_i2c_cfg = (S_I2C_Cfg*)i2c_cfg; + S_OCI2C* s_oci2c = (S_OCI2C*)oci2c; + I2C_Handle i2cHandle = i2c_open_bus(s_oc_i2c_cfg->bus); + return (i2c_reg_read(i2cHandle, s_oci2c->slaveAddress, s_oci2c->reg_address, &s_oci2c->reg_value, s_oci2c->number_of_bytes) == RETURN_OK); +} + +/***************************************************************************** + ** FUNCTION NAME : i2c_write + ** + ** DESCRIPTION : i2c write + ** + ** ARGUMENTS : i2c bus config, i2c config + ** + ** RETURN TYPE : RETURN_OK or RETURN_NOTOK + ** + *****************************************************************************/ +bool i2c_write(void* i2c_cfg, void *oci2c ) +{ + S_I2C_Cfg* s_oc_i2c_cfg = (S_I2C_Cfg*)i2c_cfg; + S_OCI2C* s_oci2c = (S_OCI2C*)oci2c; + I2C_Handle i2cHandle = i2c_open_bus(s_oc_i2c_cfg->bus); + return (i2c_reg_write(i2cHandle, s_oci2c->slaveAddress, s_oci2c->reg_address, s_oci2c->reg_value, s_oci2c->number_of_bytes) == RETURN_OK); +} diff --git a/firmware/src/devices/ocmp_wrappers/ocmp_debugocgpio.c b/firmware/src/devices/ocmp_wrappers/ocmp_debugocgpio.c new file mode 100644 index 0000000000..173efe2ff3 --- /dev/null +++ b/firmware/src/devices/ocmp_wrappers/ocmp_debugocgpio.c @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "common/inc/global/Framework.h" +#include "common/inc/global/ocmp_frame.h" +#include "inc/common/global_header.h" +#include "inc/devices/debug_ocgpio.h" +#include "inc/ocmp_wrappers/ocmp_debugocgpio.h" +#include + +#define NO_GPIO_PINS_IN_GROUP 8 +extern GPIO_PinConfig gpioPinConfigs[]; + +/***************************************************************************** + ** FUNCTION NAME : ocgpio_set + ** + ** DESCRIPTION : i2c read + ** + ** ARGUMENTS : i2c bus config, i2c config + ** + ** RETURN TYPE : RETURN_OK or RETURN_NOTOK + ** + *****************************************************************************/ +bool ocgpio_set(void* gpio_cfg, void* oc_gpio ) +{ + S_OCGPIO_Cfg* oc_gpio_cfg = (S_OCGPIO_Cfg*)gpio_cfg; + S_OCGPIO* s_oc_gpio = (S_OCGPIO*)oc_gpio; + int ret = 0; + uint8_t idx = ((oc_gpio_cfg->group != 0)?(((oc_gpio_cfg->group-1) * NO_GPIO_PINS_IN_GROUP) + s_oc_gpio->pin):s_oc_gpio->pin); + // OcGpio_Pin ocgpio = { (oc_gpio_cfg->port), idx, ((oc_gpio_cfg->group != 0)?(gpioPinConfigs[idx]>>16):OCGPIO_CFG_OUT_STD)}; + OcGpio_Pin ocgpio = { (oc_gpio_cfg->port), idx, OCGPIO_CFG_OUT_STD}; + ret = OcGpio_configure(&ocgpio, OCGPIO_CFG_OUTPUT); + ret = OcGpio_write(&ocgpio,s_oc_gpio->value); + return (ret == 0); +} + +/***************************************************************************** + ** FUNCTION NAME : ocgpio_get + ** + ** DESCRIPTION : gpio read + ** + ** ARGUMENTS : i2c bus config, i2c config + ** + ** RETURN TYPE : RETURN_OK or RETURN_NOTOK + ** + *****************************************************************************/ +bool ocgpio_get(void* gpio_cfg, void* oc_gpio ) +{ + S_OCGPIO_Cfg* oc_gpio_cfg = (S_OCGPIO_Cfg*)gpio_cfg; + S_OCGPIO* s_oc_gpio = (S_OCGPIO*)oc_gpio; + int ret = 0; + uint8_t idx = ((oc_gpio_cfg->group != 0)?(((oc_gpio_cfg->group-1) * NO_GPIO_PINS_IN_GROUP) + s_oc_gpio->pin):s_oc_gpio->pin); +// OcGpio_Pin ocgpio = { (oc_gpio_cfg->port), idx, ((oc_gpio_cfg->group!= 0)?(gpioPinConfigs[idx]>>16):OCGPIO_CFG_IN_PU)}; + OcGpio_Pin ocgpio = { (oc_gpio_cfg->port), idx, 0}; + ret = OcGpio_configure(&ocgpio, OCGPIO_CFG_INPUT); + s_oc_gpio->value = OcGpio_read(&ocgpio); + if ( s_oc_gpio->value < 0) { + ret = -1; + } + return (ret == 0); +} + +/***************************************************************************** + ** FUNCTION NAME : _probe + ** + ** DESCRIPTION : ocmp wrapper for handling POST(power on self test) + ** + ** ARGUMENTS : driver , postData + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _probe(S_OCGPIO_Cfg* oc_gpio_cfg) +{ + if (OcGpio_probe(oc_gpio_cfg->port) != 0) { + return POST_DEV_MISSING; + } else { + return POST_DEV_FOUND; + } +} + +/***************************************************************************** + ** FUNCTION NAME : _init + ** + ** DESCRIPTION : ocmp wrapper for handling init + ** + ** ARGUMENTS : driver , driver config, context for cb function + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _init(void *driver, const void *config, + const void *alert_token) +{ + return POST_DEV_CFG_DONE; +} + +const Driver_fxnTable DEBUG_OCGPIO_fxnTable = { + /* Message handlers */ + .cb_probe = _probe, + .cb_init = _init, +}; diff --git a/firmware/src/devices/ocmp_wrappers/ocmp_ina226.c b/firmware/src/devices/ocmp_wrappers/ocmp_ina226.c new file mode 100644 index 0000000000..a0540fc9c3 --- /dev/null +++ b/firmware/src/devices/ocmp_wrappers/ocmp_ina226.c @@ -0,0 +1,186 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "common/inc/global/Framework.h" +#include "common/inc/ocmp_wrappers/ocmp_ina226.h" +#include "inc/devices/ina226.h" + +typedef enum INA226Status { + INA226_STATUS_BUS_VOLTAGE = 0, + INA226_STATUS_SHUNT_VOLTAGE, + INA226_STATUS_CURRENT, + INA226_STATUS_POWER, +} INA226Status; + +typedef enum INA226Config { + INA226_CONFIG_CURRENT_LIM = 0, +} INA226Config; + +typedef enum INA226Alert { + INA226_ALERT_OVERCURRENT = 0, +} INA226Alert; + +/***************************************************************************** + ** FUNCTION NAME : _get_status + ** + ** DESCRIPTION : ocmp wrapper for getting status parameter + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _get_status(void *driver, unsigned int param_id, + void *return_buf) { + switch (param_id) { + case INA226_STATUS_BUS_VOLTAGE: { + uint16_t *res = return_buf; + return (ina226_readBusVoltage(driver, res) == RETURN_OK); + } + case INA226_STATUS_SHUNT_VOLTAGE: { + uint16_t *res = return_buf; + return (ina226_readShuntVoltage(driver, res) == RETURN_OK); + } + case INA226_STATUS_CURRENT: { + uint16_t *res = return_buf; + return (ina226_readCurrent(driver, res) == RETURN_OK); + } + case INA226_STATUS_POWER: { + uint16_t *res = return_buf; + return (ina226_readPower(driver, res) == RETURN_OK); + } + default: + LOGGER_ERROR("INA226::Unknown status param %d\n", param_id); + return false; + } +} + +/***************************************************************************** + ** FUNCTION NAME : _get_config + ** + ** DESCRIPTION : ocmp wrapper for getting config parameter + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _get_config(void *driver, unsigned int param_id, + void *return_buf) { + switch (param_id) { + case INA226_CONFIG_CURRENT_LIM: { + uint16_t *res = return_buf; + return (ina226_readCurrentLim(driver, res) == RETURN_OK); + } + default: + LOGGER_ERROR("INA226::Unknown config param %d\n", param_id); + return false; + } +} + +/***************************************************************************** + ** FUNCTION NAME : _set_config + ** + ** DESCRIPTION : ocmp wrapper for setting config parameter + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _set_config(void *driver, unsigned int param_id, + const void *data) { + switch (param_id) { + case INA226_CONFIG_CURRENT_LIM: { + const uint16_t *limit = data; + return (ina226_setCurrentLim(driver, *limit) == RETURN_OK); + } + default: + LOGGER_ERROR("INA226::Unknown config param %d\n", param_id); + return false; + } +} + +/***************************************************************************** + ** FUNCTION NAME : _probe + ** + ** DESCRIPTION : ocmp wrapper for handling POST(power on self test) + ** + ** ARGUMENTS : driver , postData + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _probe(void *driver, POSTData *postData) +{ + return ina226_probe(driver,postData); +} + +/***************************************************************************** + ** FUNCTION NAME : _alert_handler + ** + ** DESCRIPTION : call back function for handling alerts from device + ** layer + ** + ** ARGUMENTS : event type , current value, alert config, + ** + ** RETURN TYPE : + ** + *****************************************************************************/ +static void _alert_handler(INA226_Event evt, uint16_t value, void *alert_data) +{ + if (evt != INA226_EVT_COL) { + LOGGER_WARNING("IN226::Unsupported INA event 0x%x\n", evt); + return; + } + + OCMP_GenerateAlert(alert_data, INA226_ALERT_OVERCURRENT, &value); + LOGGER_DEBUG("INA226 Event: 0x%x Current: %u\n", evt, value); +} + +/***************************************************************************** + ** FUNCTION NAME : _init + ** + ** DESCRIPTION : ocmp wrapper for handling init + ** + ** ARGUMENTS : driver , driver config, context for cb function + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _init(void *driver, const void *config, + const void *alert_token) +{ + if (ina226_init(driver) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + + if (!config) { + return POST_DEV_CFG_DONE; + } + const INA226_Config *ina226_config = config; + if (ina226_setCurrentLim(driver, ina226_config->current_lim) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + + ina226_setAlertHandler(driver, _alert_handler, (void *)alert_token); + if (ina226_enableAlert(driver, INA226_EVT_COL) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + + return POST_DEV_CFG_DONE; +} + +const Driver_fxnTable INA226_fxnTable = { + /* Message handlers */ + .cb_probe = _probe, + .cb_init = _init, + .cb_get_status = _get_status, + .cb_get_config = _get_config, + .cb_set_config = _set_config, +}; diff --git a/firmware/src/devices/ocmp_wrappers/ocmp_ltc4015.c b/firmware/src/devices/ocmp_wrappers/ocmp_ltc4015.c new file mode 100644 index 0000000000..3a9e4a8646 --- /dev/null +++ b/firmware/src/devices/ocmp_wrappers/ocmp_ltc4015.c @@ -0,0 +1,350 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "common/inc/ocmp_wrappers/ocmp_ltc4015.h" +#include "inc/devices/ltc4015.h" + +typedef enum LTC4015Status { + LTC4015_STATUS_BATTERY_VOLTAGE = 0, + LTC4015_STATUS_BATTERY_CURRENT, + LTC4015_STATUS_SYSTEM_VOLTAGE, + LTC4015_STATUS_INPUT_VOLATGE, + LTC4015_STATUS_INPUT_CURRENT, + LTC4015_STATUS_DIE_TEMPERATURE, + LTC4015_STATUS_ICHARGE_DAC +} LTC4015Status; + +typedef enum LTC4015Config { + LTC4015_CONFIG_BATTERY_VOLTAGE_LOW = 0, + LTC4015_CONFIG_BATTERY_VOLTAGE_HIGH, + LTC4015_CONFIG_BATTERY_CURRENT_LOW, + LTC4015_CONFIG_INPUT_VOLTAGE_LOW, + LTC4015_CONFIG_INPUT_CURRENT_HIGH, + LTC4015_CONFIG_INPUT_CURRENT_LIMIT, + LTC4015_CONFIG_ICHARGE, + LTC4015_CONFIG_VCHARGE, + LTC4015_CONFIG_DIE_TEMPERATURE_HIGH, +} LTC4015Config; + +typedef enum LTC4015Alert { + LTC4015_ALERT_BATTERY_VOLTAGE_LOW = 0, + LTC4015_ALERT_BATTERY_VOLTAGE_HIGH, + LTC4015_ALERT_BATTERY_CURRENT_LOW, + LTC4015_ALERT_INPUT_VOLTAGE_LOW, + LTC4015_ALERT_INPUT_CURRENT_HIGH, + LTC4015_ALERT_DIE_TEMPERATURE_HIGH, +} LTC4015Alert; + +#if 0 +static bool _choose_battery_charger(LTC4015_Dev *dev) { + if (OcGpio_write(&dev->cfg.pin_lt4015_i2c_sel, + (dev->cfg.chem == LTC4015_CHEM_LI_ION)) < OCGPIO_SUCCESS) { + return false; + } + return true; +} +#endif +/***************************************************************************** + ** FUNCTION NAME : _get_status + ** + ** DESCRIPTION : ocmp wrapper for getting status + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _get_status(void *driver, unsigned int param_id, + void *return_buf) { + // if(!_choose_battery_charger(driver)) + // return false; + + switch (param_id) { + case LTC4015_STATUS_BATTERY_VOLTAGE: { + int16_t *res = return_buf; + return (LTC4015_get_battery_voltage(driver, res) == RETURN_OK); + } + case LTC4015_STATUS_BATTERY_CURRENT: { + int16_t *res = return_buf; + return (LTC4015_get_battery_current(driver, res) == RETURN_OK); + } + case LTC4015_STATUS_SYSTEM_VOLTAGE: { + int16_t *res = return_buf; + return (LTC4015_get_system_voltage(driver, res) == RETURN_OK); + } + case LTC4015_STATUS_INPUT_VOLATGE: { + int16_t *res = return_buf; + return (LTC4015_get_input_voltage(driver, res) == RETURN_OK); + } + case LTC4015_STATUS_INPUT_CURRENT: { + int16_t *res = return_buf; + return (LTC4015_get_input_current(driver, res) == RETURN_OK); + } + case LTC4015_STATUS_DIE_TEMPERATURE: { + int16_t *res = return_buf; + return (LTC4015_get_die_temperature(driver, res) == RETURN_OK); + } + case LTC4015_STATUS_ICHARGE_DAC: { + int16_t *res = return_buf; + return (LTC4015_get_icharge_dac(driver, res) == RETURN_OK); + } + default: + LOGGER_ERROR("LTC4015::Unknown status param %d\n", param_id); + return false; + } +} +/***************************************************************************** + ** FUNCTION NAME : _get_config + ** + ** DESCRIPTION : ocmp wrapper for getting config parameter + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _get_config(void *driver, unsigned int param_id, + void *return_buf) { + + switch (param_id) { + case LTC4015_CONFIG_BATTERY_VOLTAGE_LOW: { + int16_t *res = return_buf; + return (LTC4015_get_cfg_battery_voltage_low(driver, res) + == RETURN_OK); + } + case LTC4015_CONFIG_BATTERY_VOLTAGE_HIGH: { + int16_t *res = return_buf; + return (LTC4015_get_cfg_battery_voltage_high(driver, res) + == RETURN_OK); + } + case LTC4015_CONFIG_BATTERY_CURRENT_LOW: { + int16_t *res = return_buf; + return (LTC4015_get_cfg_battery_current_low(driver, res) + == RETURN_OK); + } + case LTC4015_CONFIG_INPUT_VOLTAGE_LOW: { + int16_t *res = return_buf; + return (LTC4015_get_cfg_input_voltage_low(driver, res) + == RETURN_OK); + } + case LTC4015_CONFIG_INPUT_CURRENT_HIGH: { + int16_t *res = return_buf; + return (LTC4015_get_cfg_input_current_high(driver, res) + == RETURN_OK); + } + case LTC4015_CONFIG_INPUT_CURRENT_LIMIT: { + uint16_t *res = return_buf; + return (LTC4015_get_cfg_input_current_limit(driver, res) + == RETURN_OK); + } + case LTC4015_CONFIG_ICHARGE: { + uint16_t *res = return_buf; + return (LTC4015_get_cfg_icharge(driver, res) == RETURN_OK); + } + case LTC4015_CONFIG_VCHARGE: { + uint16_t *res = return_buf; + return (LTC4015_get_cfg_vcharge(driver, res) == RETURN_OK); + } + case LTC4015_CONFIG_DIE_TEMPERATURE_HIGH: { + int16_t *res = return_buf; + return (LTC4015_get_cfg_die_temperature_high(driver, res) + == RETURN_OK); + } + default: + LOGGER_ERROR("LTC4015::Unknown config param %d\n", param_id); + return false; + } +} +/***************************************************************************** + ** FUNCTION NAME : _set_config + ** + ** DESCRIPTION : ocmp wrapper for setting config parameter + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _set_config(void *driver, unsigned int param_id, + const void *data) { + + switch (param_id) { + case LTC4015_CONFIG_BATTERY_VOLTAGE_LOW: { + const int16_t *limit = data; + return (LTC4015_cfg_battery_voltage_low(driver, *limit) + == RETURN_OK); + } + case LTC4015_CONFIG_BATTERY_VOLTAGE_HIGH: { + const int16_t *limit = data; + return (LTC4015_cfg_battery_voltage_high(driver, *limit) + == RETURN_OK); + } + case LTC4015_CONFIG_BATTERY_CURRENT_LOW: { + const int16_t *limit = data; + return (LTC4015_cfg_battery_current_low(driver, *limit) + == RETURN_OK); + } + case LTC4015_CONFIG_INPUT_VOLTAGE_LOW: { + const int16_t *limit = data; + return (LTC4015_cfg_input_voltage_low(driver, *limit) + == RETURN_OK); + } + case LTC4015_CONFIG_INPUT_CURRENT_HIGH: { + const int16_t *limit = data; + return (LTC4015_cfg_input_current_high(driver, *limit) + == RETURN_OK); + } + case LTC4015_CONFIG_INPUT_CURRENT_LIMIT: { + const uint16_t *limit = data; + return (LTC4015_cfg_input_current_limit(driver, *limit) + == RETURN_OK); + } + case LTC4015_CONFIG_ICHARGE: { + const uint16_t *limit = data; + return (LTC4015_cfg_icharge(driver, *limit) == RETURN_OK); + } + case LTC4015_CONFIG_VCHARGE: { + const uint16_t *limit = data; + return (LTC4015_cfg_vcharge(driver, *limit) == RETURN_OK); + } + case LTC4015_CONFIG_DIE_TEMPERATURE_HIGH: { + const int16_t *limit = data; + return (LTC4015_cfg_die_temperature_high(driver, *limit) + == RETURN_OK); + } + default: + LOGGER_ERROR("LTC4015::Unknown config param %d\n", param_id); + return false; + } +} +/***************************************************************************** + ** FUNCTION NAME : _probe + ** + ** DESCRIPTION : ocmp wrapper for handling POST(power on self test) + ** + ** ARGUMENTS : driver , postData + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _probe(void *driver, POSTData *postData) +{ + LTC4015_configure(driver); + return LTC4015_probe(driver, postData); +} + +static void _alert_handler(LTC4015_Event evt, int16_t value, void *alert_data) +{ + unsigned int alert; + switch (evt) { + case LTC4015_EVT_BVL: + alert = LTC4015_ALERT_BATTERY_VOLTAGE_LOW; + break; + case LTC4015_EVT_BVH: + alert = LTC4015_ALERT_BATTERY_VOLTAGE_HIGH; + break; + case LTC4015_EVT_BCL: + alert = LTC4015_ALERT_BATTERY_CURRENT_LOW; + break; + case LTC4015_EVT_IVL: + alert = LTC4015_ALERT_INPUT_VOLTAGE_LOW; + break; + case LTC4015_EVT_ICH: + alert = LTC4015_ALERT_INPUT_CURRENT_HIGH; + break; + case LTC4015_EVT_DTH: + alert = LTC4015_ALERT_DIE_TEMPERATURE_HIGH; + break; + default: + LOGGER_ERROR("Unknown LTC4015 evt: %d\n", evt); + return; + } + + OCMP_GenerateAlert(alert_data, alert, &value); + LOGGER_DEBUG("LTC4015 Event: %d Value: %d\n", evt, value); +} + +static ePostCode _init(void *driver, const void *config, + const void *alert_token) { + // if(!_choose_battery_charger(driver)) + // return false; + + if (LTC4015_init(driver) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + + if (!config) { + return POST_DEV_CFG_DONE; + } + + const LTC4015_Config *ltc4015_config = config; + + if (LTC4015_cfg_battery_voltage_low(driver, + ltc4015_config->batteryVoltageLow) + != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + if (LTC4015_cfg_battery_voltage_high(driver, + ltc4015_config->batteryVoltageHigh) + != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + if (LTC4015_cfg_battery_current_low(driver, + ltc4015_config->batteryCurrentLow) + != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + if (LTC4015_cfg_input_voltage_low(driver, ltc4015_config->inputVoltageLow) + != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + if (LTC4015_cfg_input_current_high(driver, ltc4015_config->inputCurrentHigh) + != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + if (LTC4015_cfg_input_current_limit(driver, + ltc4015_config->inputCurrentLimit) + != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + if (ltc4015_config->icharge) { + if (LTC4015_cfg_icharge(driver, ltc4015_config->icharge) + != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + } + if (ltc4015_config->vcharge) { + if (LTC4015_cfg_vcharge(driver, ltc4015_config->vcharge) + != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + } + + LTC4015_setAlertHandler(driver, _alert_handler, (void *)alert_token); + if (LTC4015_enableLimitAlerts(driver, + LTC4015_EVT_BVL | LTC4015_EVT_BVH + | LTC4015_EVT_IVL | LTC4015_EVT_ICH + | LTC4015_EVT_BCL) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + + if (LTC4015_enableChargerStateAlerts(driver, LTC4015_EVT_BMFA)) { + return POST_DEV_CFG_FAIL; + } + + return POST_DEV_CFG_DONE; +} + +const Driver_fxnTable LTC4015_fxnTable = { + /* Message handlers */ + .cb_probe = _probe, + .cb_init = _init, + .cb_get_status = _get_status, + .cb_get_config = _get_config, + .cb_set_config = _set_config, +}; diff --git a/firmware/src/devices/ocmp_wrappers/ocmp_ltc4274.c b/firmware/src/devices/ocmp_wrappers/ocmp_ltc4274.c new file mode 100644 index 0000000000..45010077ce --- /dev/null +++ b/firmware/src/devices/ocmp_wrappers/ocmp_ltc4274.c @@ -0,0 +1,340 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "common/inc/ocmp_wrappers/ocmp_ltc4274.h" +#include "helpers/array.h" +#include "helpers/math.h" +#include "inc/devices/ltc4274.h" + +typedef enum LTC7274Status { + LTC7274_STATUS_DETECT = 0, + LTC7274_STATUS_CLASS , + LTC7274_STATUS_POWERGOOD, +} LTC7274Status; + +typedef enum LTC7274Config { + LTC4274_CONFIG_OPERATING_MODE = 0, + LTC4274_CONFIG_DETECT_ENABLE, + LTC4274_CONFIG_INTERRUPT_MASK, + LTC4274_CONFIG_INTERRUPT_ENABLE, + LTC4274_CONFIG_HP_ENABLE +} LTC7274Config; + +typedef enum LTC7274Alert { + LTC4274_ALERT_NO_ACTIVE = 0, + LTC4274_ALERT_POWER_ENABLE, + LTC4274_ALERT_POWERGOOD, + LTC4274_ALERT_DISCONNECT, + LTC4274_ALERT_DETECTION , + LTC4274_ALERT_CLASS, + LTC4274_ALERT_TCUT, + LTC4274_ALERT_TSTART, + LTC4274_ALERT_SUPPLY +} LTC7274Alert; + +bool LTC4274_reset(void *driver, void *params) +{ + ReturnStatus status = RETURN_OK; + status = ltc4274_reset(); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : _get_status + ** + ** DESCRIPTION : ocmp wrapper for getting status + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _get_status(void *driver, unsigned int param_id, + void *return_buf) { + bool ret = true; + uint8_t *res = return_buf; + switch (param_id) { + case LTC7274_STATUS_DETECT: + { + if (ltc4274_get_detection_status(driver, res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: Reading PSE detection and classification failed.\n"); + } + break; + } + case LTC7274_STATUS_CLASS: + { + if (ltc4274_get_class_status(driver, res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: Reading PSE power status failed.\n"); + } + break; + } + case LTC7274_STATUS_POWERGOOD: + { + if (ltc4274_get_powergood_status(driver, res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: Reading PSE power status failed.\n"); + return ret; + } + break; + } + default: + { + LOGGER("LTC4274:ERROR::Unkown parameter recived for PSE status.\n"); + ret = false; + } + } + return ret; +} + +/***************************************************************************** + ** FUNCTION NAME : _set_config + ** + ** DESCRIPTION : ocmp wrapper for setting config parameter + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _set_config(void *driver, unsigned int param_id, + const void *data) { + bool ret = true; + uint8_t *res = (uint8_t*)data; + switch (param_id) { + case LTC4274_CONFIG_OPERATING_MODE: + { + if ( ltc4274_set_cfg_operation_mode(driver, *res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: PSE operational mode setting mode failed.\n"); + } + break; + } + case LTC4274_CONFIG_DETECT_ENABLE: + { + if (ltc4274_set_cfg_detect_enable(driver, *res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: PSE detection and classification enable failed.\n"); + } + break; + } + case LTC4274_CONFIG_INTERRUPT_MASK: + { + if (ltc4274_set_interrupt_mask(driver, *res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR::PSE interrupts mask enable failed.\n"); + } + break; + } + case LTC4274_CONFIG_INTERRUPT_ENABLE: + { + if (ltc4274_cfg_interrupt_enable(driver, *res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: PSE interrupt enable failed.\n"); + } + break; + } + case LTC4274_CONFIG_HP_ENABLE: + { + if (ltc4274_set_cfg_pshp_feature(driver, *res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: PSE configuration for LTEPOE++ failed.\n"); + } + break; + } + default: + { + LOGGER("LTC4274:ERROR:: PSE configurion unkown parmeter passed.\n"); + ret = false; + } + } + return ret; +} + +/***************************************************************************** + ** FUNCTION NAME : _get_config + ** + ** DESCRIPTION : ocmp wrapper for getting config parameter + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _get_config(void *driver, unsigned int param_id, + void *return_buf) { + bool ret = true; + uint8_t *res = return_buf; + switch (param_id) { + case LTC4274_CONFIG_OPERATING_MODE: + { + if (ltc4274_get_operation_mode(driver, res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: PSE operational mode setting mode failed.\n"); + } + break; + } + case LTC4274_CONFIG_DETECT_ENABLE: + { + if (ltc4274_get_detect_enable(driver, res)!= RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: PSE detection and classification enable failed.\n"); + } + break; + } + case LTC4274_CONFIG_INTERRUPT_MASK: + { + if (ltc4274_get_interrupt_mask(driver, res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR::PSE interrupts mask enable failed.\n"); + } + break; + } + case LTC4274_CONFIG_INTERRUPT_ENABLE: + { + if (ltc4274_get_interrupt_enable(driver, res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: PSE interrupt enable failed.\n"); + } + break; + } + case LTC4274_CONFIG_HP_ENABLE: + { + if (ltc4274_get_pshp_feature(driver, res) != RETURN_OK) { + ret = false; + LOGGER("LTC4274:ERROR:: PSE configuration for LTEPOE++ failed.\n"); + } + break; + } + default: + { + LOGGER("LTC4274:ERROR:: PSE configurion unkown parmeter passed.\n"); + ret = false; + } + } + return ret; +} + +/***************************************************************************** + ** FUNCTION NAME : _probe + ** + ** DESCRIPTION : ocmp wrapper for handling POST(power on self test) + ** + ** ARGUMENTS : driver , postData + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _probe(void *driver, POSTData *postData) +{ + ltc4274_config(driver); + return ltc4274_probe(driver, postData); + +} + +/***************************************************************************** + ** FUNCTION NAME : _alert_handler + ** + ** DESCRIPTION : call back function for handling alerts from device + ** layer + ** + ** ARGUMENTS : event type , current value, alert config, + ** + ** RETURN TYPE : + ** + *****************************************************************************/ +static void _alert_handler(LTC4274_Event evt, + void *context) +{ + unsigned int alert; + switch(evt) { + case LTC4274_EVT_SUPPLY: + alert = LTC4274_ALERT_SUPPLY; + break; + case LTC4274_EVT_TSTART: + alert = LTC4274_ALERT_TSTART; + break; + case LTC4274_EVT_TCUT: + alert = LTC4274_ALERT_TCUT; + break; + case LTC4274_EVT_CLASS: + alert = LTC4274_ALERT_CLASS; + break; + case LTC4274_EVT_DETECTION: + alert = LTC4274_ALERT_DETECTION; + break; + case LTC4274_EVT_DISCONNECT: + alert = LTC4274_ALERT_DISCONNECT; + break; + case LTC4274_EVT_POWERGOOD: + alert = LTC4274_ALERT_POWERGOOD; + break; + case LTC4274_EVT_POWER_ENABLE: + alert = LTC4274_ALERT_POWER_ENABLE; + break; + default: + { + alert = LTC4274_ALERT_NO_ACTIVE; + return; + } + } + uint8_t alert_data = 0x00; + OCMP_GenerateAlert(context, alert, &alert_data); + LOGGER_DEBUG("LTC7274 Event: %d \n", evt); +} + +/***************************************************************************** + ** FUNCTION NAME : _init + ** + ** DESCRIPTION : ocmp wrapper for handling init + ** + ** ARGUMENTS : driver , driver config, context for cb function + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _init(void *driver, const void *config, + const void *alert_token) +{ + ltc4274_init(driver); + + if (!config) { + return POST_DEV_CFG_DONE; + } + const LTC4274_Config *LTC7274_config = config; + if ( ltc4274_set_cfg_operation_mode(driver,LTC7274_config->operatingMode) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + if ( ltc4274_set_cfg_detect_enable(driver,LTC7274_config->detectEnable) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + if ( ltc4274_set_interrupt_mask(driver,LTC7274_config->interruptMask) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + if ( ltc4274_set_cfg_pshp_feature(driver,LTC7274_config->pseHpEnable) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + ltc4274_set_alert_handler(driver, _alert_handler, (void *)alert_token); + //TODO: SET enable or disable. + if (ltc4274_cfg_interrupt_enable(driver, LTC7274_config->interruptEnable) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + ltc4274_update_stateInfo(driver); + + return POST_DEV_CFG_DONE; +} + +const Driver_fxnTable LTC4274_fxnTable = { + /* Message handlers */ + .cb_probe = _probe, + .cb_init = _init, + .cb_get_status = _get_status, + .cb_get_config = _get_config, + .cb_set_config = _set_config, +}; diff --git a/firmware/src/devices/ocmp_wrappers/ocmp_ltc4295.c b/firmware/src/devices/ocmp_wrappers/ocmp_ltc4295.c new file mode 100644 index 0000000000..025ac79ef6 --- /dev/null +++ b/firmware/src/devices/ocmp_wrappers/ocmp_ltc4295.c @@ -0,0 +1,128 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "common/inc/ocmp_wrappers/ocmp_ltc4295.h" +#include "helpers/array.h" +#include "helpers/math.h" +#include "inc/devices/ltc4295.h" + +/***************************************************************************** + ** FUNCTION NAME : _get_status + ** + ** DESCRIPTION : ocmp wrapper for getting status + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _get_status(void *driver, unsigned int param_id, + void *return_buf) +{ + bool ret = false; + switch (param_id) { + case LTC4295_STATUS_CLASS: + { + ePDClassType *res = (ePDClassType*)return_buf; + if (ltc4295_get_class(driver, res) == RETURN_OK) { + ret = true; + } + } + break; + case LTC4295_STATUS_POWERGOOD: + { + ePDPowerState *res =(ePDPowerState*) return_buf; + if (ltc4295_get_power_good(driver, res) == RETURN_OK) { + ret = true; + } + break; + } + default: + { + LOGGER_ERROR("LTC4295::Unknown status param %d\n", param_id); + ret = false; + } + } + return ret; +} + +/***************************************************************************** + ** FUNCTION NAME : _probe + ** + ** DESCRIPTION : ocmp wrapper for handling POST(power on self test) + ** + ** ARGUMENTS : driver , postData + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _probe(void *driver, POSTData *postData) +{ + ltc4295_config(driver); + return ltc4295_probe(driver,postData); +} + +/***************************************************************************** + ** FUNCTION NAME : _alert_handler + ** + ** DESCRIPTION : call back function for handling alerts from device + ** layer + ** + ** ARGUMENTS : event type , current value, alert config, + ** + ** RETURN TYPE : + ** + *****************************************************************************/ +static void _alert_handler(LTC4295_Event evt, void *context) +{ + unsigned int alert; + switch (evt) { + case LTC4295_CONNECT_EVT: + alert = LTC4295_CONNECT_ALERT; + break; + case LTC4295_DISCONNECT_EVT: + alert = LTC4295_DISCONNECT_ALERT; + break; + case LTC4295_INCOMPATIBLE_EVT: + alert = LTC4295_INCOMPATIBLE_ALERT; + break; + default: + LOGGER_ERROR("Unknown LTC4295evt: %d\n", evt); + return; + } + OCMP_GenerateAlert(context, alert, &evt); + LOGGER_DEBUG("LTC4295A alert: %d generated.\n", alert); +} + +/***************************************************************************** + ** FUNCTION NAME : _init + ** + ** DESCRIPTION : ocmp wrapper for handling init + ** + ** ARGUMENTS : driver , driver config, context for cb function + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _init(void *driver, const void *config, + const void *alert_token) +{ + if (ltc4295_init(driver) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + + ltc4295_set_alert_handler(driver, _alert_handler, (void *)alert_token); + return POST_DEV_CFG_DONE; +} + +const Driver_fxnTable LTC4295_fxnTable = { + /* Message handlers */ + .cb_probe = _probe, + .cb_init = _init, + .cb_get_status = _get_status, +}; diff --git a/firmware/src/devices/ocmp_wrappers/ocmp_powerSource.c b/firmware/src/devices/ocmp_wrappers/ocmp_powerSource.c new file mode 100644 index 0000000000..6267b5cda1 --- /dev/null +++ b/firmware/src/devices/ocmp_wrappers/ocmp_powerSource.c @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "common/inc/global/Framework.h" +#include "common/inc/ocmp_wrappers/ocmp_powersource.h" +#include "helpers/array.h" +#include "inc/devices/powerSource.h" + +/***************************************************************************** + ** FUNCTION NAME : _get_status + ** + ** DESCRIPTION : ocmp wrapper for getting status + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _get_status(void *driver, unsigned int param_id, + void *return_buf) +{ + bool ret = false; + pwr_get_source_info(driver); + if ( pwr_process_get_status_parameters_data(param_id,return_buf) == RETURN_OK) { + ret = true; + } + return ret; +} + +/***************************************************************************** + ** FUNCTION NAME : _probe + ** + ** DESCRIPTION : ocmp wrapper for handling POST(power on self test) + ** + ** ARGUMENTS : driver , postData + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _probe(void *driver) +{ + /* powersource is part of TIVA */ + return POST_DEV_FOUND; +} + +/***************************************************************************** + ** FUNCTION NAME : _init + ** + ** DESCRIPTION : ocmp wrapper for handling init + ** + ** ARGUMENTS : driver , driver config, context for cb function + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _init(void *driver, const void *config, + const void *alert_token) +{ + pwr_source_init(); + return POST_DEV_NO_CFG_REQ; +} + +const Driver_fxnTable PWRSRC_fxnTable = { + /* Message handlers */ + .cb_probe = _probe, + .cb_init = _init, + .cb_get_status = _get_status, +}; diff --git a/firmware/src/devices/ocmp_wrappers/ocmp_se98a.c b/firmware/src/devices/ocmp_wrappers/ocmp_se98a.c new file mode 100644 index 0000000000..bb7a13529b --- /dev/null +++ b/firmware/src/devices/ocmp_wrappers/ocmp_se98a.c @@ -0,0 +1,207 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "common/inc/ocmp_wrappers/ocmp_se98a.h" +#include "helpers/array.h" +#include "helpers/math.h" +#include "inc/devices/se98a.h" + +typedef enum Se98aStatus { + SE98A_STATUS_TEMPERATURE = 0, +} Se98aStatus; + +typedef enum Se98aConfig { + SE98A_CONFIG_LIM_LOW = 0, + SE98A_CONFIG_LIM_HIGH, + SE98A_CONFIG_LIM_CRIT, +} Se98aConfig; + +typedef enum Se98aAlert { + SE98A_ALERT_LOW = 0, + SE98A_ALERT_HIGH, + SE98A_ALERT_CRITICAL +} Se98aAlert; + +/***************************************************************************** + ** FUNCTION NAME : _get_status + ** + ** DESCRIPTION : ocmp wrapper for getting status + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _get_status(void *driver, unsigned int param_id, + void *return_buf) { + switch (param_id) { + case SE98A_STATUS_TEMPERATURE: { + int8_t *res = return_buf; + if (se98a_read(driver, res) == RETURN_OK) { + return true; + } + break; + } + default: + LOGGER_ERROR("SE98A::Unknown status param %d\n", param_id); + return false; + } + return false; +} + +/***************************************************************************** + ** FUNCTION NAME : _get_config + ** + ** DESCRIPTION : ocmp wrapper for getting config parameter + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _get_config(void *driver, unsigned int param_id, + void *return_buf) { + switch (param_id) { + case SE98A_CONFIG_LIM_LOW: + case SE98A_CONFIG_LIM_HIGH: + case SE98A_CONFIG_LIM_CRIT: { + int8_t *res = return_buf; + if (se98a_get_limit(driver, param_id + 1, res) == RETURN_OK) { + return true; + } + break; + } + default: + LOGGER_ERROR("SE98A::Unknown config param %d\n", param_id); + return false; + } + return false; +} + +/***************************************************************************** + ** FUNCTION NAME : _set_config + ** + ** DESCRIPTION : ocmp wrapper for setting config parameter + ** + ** ARGUMENTS : driver , parameter id in ocmp msg , ocmp payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _set_config(void *driver, unsigned int param_id, + const void *data) { + switch (param_id) { + case SE98A_CONFIG_LIM_LOW: + case SE98A_CONFIG_LIM_HIGH: + case SE98A_CONFIG_LIM_CRIT: { + const int8_t *limit = data; + if (se98a_set_limit(driver, param_id + 1, *limit) == RETURN_OK) { + return true; + } + break; + } + default: + LOGGER_ERROR("SE98A::Unknown config param %d\n", param_id); + return false; + } + return false; +} + +/***************************************************************************** + ** FUNCTION NAME : _probe + ** + ** DESCRIPTION : ocmp wrapper for handling POST(power on self test) + ** + ** ARGUMENTS : driver , postData + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _probe(void *driver, POSTData *postData) +{ + return se98a_probe(driver, postData); +} + +/***************************************************************************** + ** FUNCTION NAME : _alert_handler + ** + ** DESCRIPTION : call back function for handling alerts from device + ** layer + ** + ** ARGUMENTS : event type , current value, alert config, + ** + ** RETURN TYPE : + ** + *****************************************************************************/ +static void _alert_handler(SE98A_Event evt, int8_t temperature, + void *context) +{ + unsigned int alert; + switch (evt) { + case SE98A_EVT_ACT: + alert = SE98A_ALERT_CRITICAL; + break; + case SE98A_EVT_AAW: + alert = SE98A_ALERT_HIGH; + break; + case SE98A_EVT_BAW: + alert = SE98A_ALERT_LOW; + break; + default: + LOGGER_ERROR("Unknown SE98A evt: %d\n", evt); + return; + } + + uint8_t alert_data = (uint8_t)MAX((int8_t)0, temperature); + OCMP_GenerateAlert(context, alert, &alert_data); + LOGGER_DEBUG("SE98A Event: %d Temperature: %d\n", evt, temperature); +} + +/***************************************************************************** + ** FUNCTION NAME : _init + ** + ** DESCRIPTION : ocmp wrapper for handling init + ** + ** ARGUMENTS : driver , driver config, context for cb function + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _init(void *driver, const void *config, + const void *alert_token) +{ + if (se98a_init(driver) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + if (!config) { + return POST_DEV_CFG_DONE; + } + const SE98A_Config *se98a_config = config; + for (int i = 0; i < ARRAY_SIZE(se98a_config->limits); ++i) { + if (se98a_set_limit(driver, i + 1, se98a_config->limits[i]) != + RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + } + + se98a_set_alert_handler(driver, _alert_handler, (void *)alert_token); + if (se98a_enable_alerts(driver) != RETURN_OK) { + return POST_DEV_CFG_FAIL; + } + + return POST_DEV_CFG_DONE; +} + +const Driver_fxnTable SE98_fxnTable = { + /* Message handlers */ + .cb_probe = _probe, + .cb_init = _init, + .cb_get_status = _get_status, + .cb_get_config = _get_config, + .cb_set_config = _set_config, +}; diff --git a/firmware/src/devices/powerSource.c b/firmware/src/devices/powerSource.c new file mode 100644 index 0000000000..9164d8cfce --- /dev/null +++ b/firmware/src/devices/powerSource.c @@ -0,0 +1,278 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** + +#include "Board.h" +#include "inc/common/global_header.h" +#include "inc/common/i2cbus.h" +#include "inc/devices/powerSource.h" +#include "inc/subsystem/power/power.h" + +#include +#include + +static tPowerSource Power_SourceInfo[PWR_SRC_MAX]; + +/***************************************************************************** + ** FUNCTION NAME : pwr_update_source_info + ** + ** DESCRIPTION : Update power source information. + ** + ** ARGUMENTS : Power Source and Power source state. + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void pwr_update_source_info(ePowerSource powerSrc, ePowerSourceState pwrState) +{ + + ePowerSource itr = PWR_SRC_EXT ; + for (; itr < PWR_SRC_MAX; itr++) { + if (Power_SourceInfo[itr].powerSource == powerSrc) { + Power_SourceInfo[itr].state = pwrState; + LOGGER("POWER:INFO:: Power State updated for Power Source %d with %d.\n", + Power_SourceInfo[itr].powerSource, + Power_SourceInfo[itr].state); + } + } +} + +/****************************************************************************** + * @fn pwr_check_poe + * + * @brief Check presence of POE. + * + * @args None. + * + * @return ReturnStatus + ******************************************************************************/ +static ReturnStatus pwr_check_poe(PWRSRC_Dev *pwrSrcDev) +{ + ReturnStatus ret = RETURN_NOTOK; + uint8_t value = 0; + ePowerSourceState status = PWR_SRC_NON_AVAILABLE; + //For Checking POE POWER SOURCE + value = OcGpio_read(&pwrSrcDev->cfg.pin_poe_prsnt_n); + if ( value == 0) { + status=PWR_SRC_AVAILABLE; + ret = RETURN_OK; + } + pwr_update_source_info(PWR_SRC_POE, status); + return ret; +} + +/****************************************************************************** + * @fn pwr_check_ext_power + * + * @brief Check presence of external power. + * + * @args None. + * + * @return ReturnStatus + ******************************************************************************/ +static ReturnStatus pwr_check_ext_power(PWRSRC_Dev *pwrSrcDev) +{ + ReturnStatus ret = RETURN_NOTOK; + uint8_t value = 0; + ePowerSourceState status = PWR_SRC_NON_AVAILABLE; + //For Checking POE POWER SOURCE + value = OcGpio_read(&pwrSrcDev->cfg.pin_dc_present); + if ( value == 0) { + status=PWR_SRC_AVAILABLE; + ret = RETURN_OK; + } + pwr_update_source_info(PWR_SRC_EXT, status); + return ret; +} + +/****************************************************************************** + * @fn pwr_check_batt + * + * @brief Check presence of Battery. + * + * @args None. + * + * @return ReturnStatus + ******************************************************************************/ +static ReturnStatus pwr_check_batt(PWRSRC_Dev *pwrSrcDev) +{ + ReturnStatus ret = RETURN_NOTOK; + uint8_t value = 0; + ePowerSourceState status = PWR_SRC_NON_AVAILABLE; + //For Checking POE POWER SOURCE + value = OcGpio_read(&pwrSrcDev->cfg.pin_int_bat_prsnt); + if ( value == 0) { + status=PWR_SRC_AVAILABLE; + ret = RETURN_OK; + } + pwr_update_source_info(PWR_SRC_LIION_BATT, status); + return ret; +} + +/****************************************************************************** + ** FUNCTION NAME : pwr_check_presence_of_source + ** + ** DESCRIPTION : check for power source available. + ** + ** ARGUMENTS : pointer to powersource config + ** + ** RETURN TYPE : None + ** + ******************************************************************************/ +static void pwr_check_presence_of_source(PWRSRC_Dev *pwrSrcDev) +{ + ReturnStatus ret = RETURN_NOTOK; + ret = pwr_check_ext_power(pwrSrcDev); + LOGGER("POWER:INFO:: Power Source External %s.\n", + ((ret == RETURN_OK) ? "available" : "not available")); + + ret = pwr_check_poe(pwrSrcDev); + LOGGER("POWER:INFO:: Power Source POE %s.\n", + ((ret == RETURN_OK) ? "available" : "not available")); + + ret = pwr_check_batt(pwrSrcDev); + LOGGER("POWER:INFO:: Power Source BATTERY %s.\n", + ((ret == RETURN_OK) ? "available" : "not available")); + + return ; +} + +/****************************************************************************** + ** FUNCTION NAME : pwr_source_inuse + ** + ** DESCRIPTION : Give info about currently used power source. + ** + ** ARGUMENTS : output pointer for storing powersource + ** + ** RETURN TYPE : RETURN_OK or RETURN_NOTOK + ** + ******************************************************************************/ +static ReturnStatus pwr_source_inuse(ePowerSource *inUse) +{ + ReturnStatus ret = RETURN_NOTOK; + ePowerSource itr = PWR_SRC_EXT ; + for ( ; itr < PWR_SRC_MAX; itr++) { + if (Power_SourceInfo[itr].state == PWR_SRC_AVAILABLE) { + *inUse = itr; + ret = RETURN_OK; + break; + } + + } + return ret; +} + +/***************************************************************************** + ** FUNCTION NAME : pwr_source_init + ** + ** DESCRIPTION : initialize power source information. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void pwr_source_init() +{ + ePowerSource itr = PWR_SRC_EXT ; + for (; itr < PWR_SRC_MAX; itr++) { + Power_SourceInfo[itr].powerSource = itr; + Power_SourceInfo[itr].state = PWR_SRC_NON_AVAILABLE; + } + + return; +} + +/***************************************************************************** + ** FUNCTION NAME : pwr_get_source_info + ** + ** DESCRIPTION : initialize power source information. + ** + ** ARGUMENTS : power source config + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void pwr_get_source_info(PWRSRC_Dev *pwrSrcDev) +{ + ReturnStatus status = RETURN_NOTOK; + ePowerSource powerSource = PWR_SRC_EXT; + /* Check the presence of power sources*/ + pwr_check_presence_of_source(pwrSrcDev); + + /* Find the primary power source and update Power Source info for same.*/ + status = pwr_source_inuse(&powerSource); + if (status != RETURN_OK) { + LOGGER("POWER:ERROR:: Failed to get current power source.\n"); + } else { + LOGGER("POWER:INFO:: Current Power source is 0x%x.\n", powerSource); + pwr_update_source_info(powerSource, PWR_SRC_ACTIVE); + } +} + +/***************************************************************************** + ** FUNCTION NAME : pwr_process_get_status_parameters_data + ** + ** DESCRIPTION : Get Power Status Message. + ** + ** ARGUMENTS : parameter id ,Pointer to OCMPMessageFrame payload + ** + ** RETURN TYPE : RETURN_OK or RETURN_NOTOK + ** + *****************************************************************************/ +ReturnStatus +pwr_process_get_status_parameters_data(ePower_StatusParamId paramIndex, + uint8_t *pPowerStatusData) +{ + ReturnStatus status = RETURN_OK; + switch (paramIndex) { + case PWR_STAT_EXT_PWR_AVAILABILITY: { + if ((Power_SourceInfo[PWR_SRC_POE].state == PWR_SRC_ACTIVE) || + (Power_SourceInfo[PWR_SRC_POE].state == PWR_SRC_AVAILABLE)) + *pPowerStatusData = 1; + break; + } + case PWR_STAT_EXT_PWR_ACTIVE: { + if (Power_SourceInfo[PWR_SRC_POE].state == PWR_SRC_ACTIVE) + *pPowerStatusData = 1; + break; + } + case PWR_STAT_POE_AVAILABILITY: { + if ((Power_SourceInfo[PWR_SRC_POE].state == PWR_SRC_ACTIVE) || + (Power_SourceInfo[PWR_SRC_POE].state == PWR_SRC_AVAILABLE)) + *pPowerStatusData = 1; + break; + } + case PWR_STAT_POE_ACTIVE: { + if (Power_SourceInfo[PWR_SRC_POE].state == PWR_SRC_ACTIVE) + *pPowerStatusData = 1; + break; + } + case PWR_STAT_BATT_AVAILABILITY: { + if ((Power_SourceInfo[PWR_SRC_LIION_BATT].state == + PWR_SRC_ACTIVE) || + (Power_SourceInfo[PWR_SRC_LIION_BATT].state == + PWR_SRC_AVAILABLE)) + *pPowerStatusData = 1; + break; + } + case PWR_STAT_BATT_ACTIVE: { + if (Power_SourceInfo[PWR_SRC_LIION_BATT].state == PWR_SRC_ACTIVE) + *pPowerStatusData = 1; + break; + } + default: { + LOGGER("POWER::ERROR: Invalid Power param status.\n"); + } + } + return status; +} diff --git a/firmware/src/devices/se98a.c b/firmware/src/devices/se98a.c new file mode 100644 index 0000000000..9f42aebe9f --- /dev/null +++ b/firmware/src/devices/se98a.c @@ -0,0 +1,405 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "devices/i2c/threaded_int.h" +#include "helpers/math.h" +#include "helpers/memory.h" +#include "inc/common/byteorder.h" +#include "inc/devices/se98a.h" + +#include +#include + +/***************************************************************************** + * Register Definitions + *****************************************************************************/ +/* Register Addresses */ +#define SE98A_REG_CAPS 0x00 +#define SE98A_REG_CFG 0x01 +#define SE98A_REG_HIGH_LIM 0x02 +#define SE98A_REG_LOW_LIM 0x03 +#define SE98A_REG_CRIT_LIM 0x04 +#define SE98A_REG_TEMP 0x05 +#define SE98A_REG_MFG_ID 0x06 +#define SE98A_REG_DEV_ID 0x07 + +/* Temperature Sensor Info */ +#define SE98A_MFG_ID 0x1131 +#define SE98A_DEV_ID 0xA1 + +/* Configuration Bits */ +#define SE98A_CFG_HEN_H (1 << 10) /* Hysteresis Enable High Bit */ +#define SE98A_CFG_HEN_L (1 << 9) /* Hysteresis Enable Low Bit */ +#define SE98A_CFG_SHMD (1 << 8) /* Shutdown Mode */ + +#define SE98A_CFG_CTLB (1 << 7) /* Critical Trip Lock Bit */ +#define SE98A_CFG_AWLB (1 << 6) /* Alarm Window Lock Bit */ +#define SE98A_CFG_CEVENT (1 << 5) /* (WO) Clear EVENT */ +#define SE98A_CFG_ESTAT (1 << 4) /* (RO) EVENT Status */ + +#define SE98A_CFG_EOCTL (1 << 3) /* EVENT Output Control */ +#define SE98A_CFG_CVO (1 << 2) /* Critical Event Only */ +#define SE98A_CFG_EP (1 << 1) /* EVENT Polarity */ +#define SE98A_CFG_EMD (1 << 0) /* EVENT Mode */ + +#define SE98A_CFG_HYS_0 (0x0 << 9) +#define SE98A_CFG_HYS_1P5 (0x1 << 9) +#define SE98A_CFG_HYS_3 (0x2 << 9) +#define SE98A_CFG_HYS_6 (0x3 << 9) + +/* Default CFG plus interrupt mode (we don't support comparator mode) */ +#define SE98A_CONFIG_DEFAULT (0x0000 | SE98A_CFG_EMD | SE98A_CFG_HYS_1P5) + +/***************************************************************************** + * Helper to read from a SE98A register + *****************************************************************************/ +static ReturnStatus se98a_reg_read(const SE98A_Dev *dev, + uint8_t regAddress, + uint16_t *regValue) +{ + ReturnStatus status = RETURN_NOTOK; + I2C_Handle tempHandle = i2c_get_handle(dev->cfg.dev.bus); + if (!tempHandle) { + LOGGER_ERROR("SE98A:ERROR:: Failed to get I2C Bus for Temperature " + "sensor 0x%x on bus 0x%x.\n", dev->cfg.dev.slave_addr, + dev->cfg.dev.bus); + } else { + status = i2c_reg_read(tempHandle, dev->cfg.dev.slave_addr, regAddress, + regValue, 2); + *regValue = betoh16(*regValue); + } + return status; +} + +/***************************************************************************** + * Helper to write to a SE98A register + *****************************************************************************/ +static ReturnStatus se98a_reg_write(const SE98A_Dev *dev, + uint8_t regAddress, + uint16_t regValue) +{ + ReturnStatus status = RETURN_NOTOK; + I2C_Handle tempHandle = i2c_get_handle(dev->cfg.dev.bus); + if (!tempHandle) { + LOGGER_ERROR("SE98A:ERROR:: Failed to get I2C Bus for Temperature " + "sensor 0x%x on bus 0x%x.\n", dev->cfg.dev.slave_addr, + dev->cfg.dev.bus); + } else { + regValue = htobe16(regValue); + status = i2c_reg_write(tempHandle, dev->cfg.dev.slave_addr, regAddress, + regValue, 2); + } + return status; +} + +/***************************************************************************** + * Helper to read the device ID + *****************************************************************************/ +static ReturnStatus se98a_get_dev_id(const SE98A_Dev *dev, uint8_t *devID) +{ + uint16_t regValue; + ReturnStatus status = se98a_reg_read(dev, SE98A_REG_DEV_ID, + ®Value); + if (status == RETURN_OK) { + /* Strip off the revision - we don't care about it right now */ + *devID = HIBYTE(regValue); + } + return status; +} + +/***************************************************************************** + * Helper to read the manufacturer ID + *****************************************************************************/ +static ReturnStatus se98a_get_mfg_id(const SE98A_Dev *dev, uint16_t *mfgID) +{ + return se98a_reg_read(dev, SE98A_REG_MFG_ID, mfgID); +} + +/***************************************************************************** + * Helper to write to the configuration register + *****************************************************************************/ +static ReturnStatus se98a_set_config_reg(const SE98A_Dev *dev, + uint16_t configValue) +{ + return se98a_reg_write(dev, SE98A_REG_CFG, configValue); +} + +/***************************************************************************** + *****************************************************************************/ +ReturnStatus se98a_set_limit(SE98A_Dev *dev, + eTempSensor_ConfigParamsId limitToConfig, + int8_t tempLimitValue) +{ + uint8_t regAddress = 0x00; + + /* Get the limit to configure */ + switch (limitToConfig) { + case CONF_TEMP_SE98A_LOW_LIMIT_REG: + regAddress = SE98A_REG_LOW_LIM; + break; + case CONF_TEMP_SE98A_HIGH_LIMIT_REG: + regAddress = SE98A_REG_HIGH_LIM; + break; + case CONF_TEMP_SE98A_CRITICAL_LIMIT_REG: + regAddress = SE98A_REG_CRIT_LIM; + break; + default: + return RETURN_NOTOK; + } + + /* + * [15..13] RFU + * [12] SIGN (2's complement) + * [11..4] Integer part (8 bits) + * [3..2] Fractional part (0.5, 0.25) + * [1..0] RFU + */ + + /* The device technically takes an int9, but is only rated from + * -40 to +125, so we'll settle for an int8 */ + uint16_t regValue = ((int16_t)tempLimitValue & 0x00FF) << 4; + + /* Set the sign bit if negative */ + if (tempLimitValue < 0) { + regValue |= 0x1000; + } + + return se98a_reg_write(dev, regAddress, regValue); +} + +/***************************************************************************** + * Helper to convert a SE98A register value to a temperature + *****************************************************************************/ +static int8_t reg2temp(uint16_t reg) { + /* The limit regs have lower precision, so by making this function common, + * we lose 0.125 precision... since we round to the nearest int, I'm not + * worried */ + + /* Calculate the Actual Temperature Value from fixed point register + * (bottom 4 bits are fractional part - divide by 16) + * + * Register map REG_TEMP / REG_LIM + * [15..13] Trip Status / RFU + * [12] Sign / Sign + * [11..5] 8-bit Int / 8-bit Int + * [4..2] Fraction / Fraction + * [1] Fraction / RFU + * [0] RFU + */ + int16_t temperature = (reg & 0x0FFC); + + /* If negative, upper bits must be set (assume a 2's comp. system) */ + if (reg & 0x1000) { + temperature |= 0xF000; + } + temperature = round(temperature / 16.0f); + + return CONSTRAIN(temperature, INT8_MIN, INT8_MAX); +} + +/***************************************************************************** + *****************************************************************************/ +ReturnStatus se98a_read(SE98A_Dev *dev, int8_t *tempValue) +{ + /* The temperature value is 2's complement with the LSB equal to 0.0625. + * The resolution is 0.125 (bit 0 is unused) + */ + uint16_t regValue = 0x0000; + ReturnStatus status = se98a_reg_read(dev, SE98A_REG_TEMP, ®Value); + if (status == RETURN_OK) { + *tempValue = reg2temp(regValue); + LOGGER_DEBUG("TEMPSENSOR:INFO:: Temperature sensor 0x%x on bus " + "0x%x is reporting Temperature value of %d Celsius.\n", + dev->cfg.dev.slave_addr, dev->cfg.dev.bus, *tempValue); + } + return status; +} + + +/***************************************************************************** + * Helper to read the configuration register + *****************************************************************************/ +static ReturnStatus se98a_get_config_reg(const SE98A_Dev *dev, + uint16_t *configValue) +{ + return se98a_reg_read(dev, SE98A_REG_CFG, configValue); +} + +/***************************************************************************** + *****************************************************************************/ +ReturnStatus se98a_get_limit(SE98A_Dev *dev, + eTempSensor_ConfigParamsId limitToConfig, + int8_t* tempLimitValue) +{ + ReturnStatus status = RETURN_NOTOK; + uint16_t regValue = 0x0000; + uint8_t regAddress = 0x0000; + + /*getting the limit to configure */ + switch (limitToConfig) { + case CONF_TEMP_SE98A_LOW_LIMIT_REG: + regAddress = SE98A_REG_LOW_LIM; + break; + case CONF_TEMP_SE98A_HIGH_LIMIT_REG: + regAddress = SE98A_REG_HIGH_LIM; + break; + case CONF_TEMP_SE98A_CRITICAL_LIMIT_REG: + regAddress = SE98A_REG_CRIT_LIM; + break; + default: + return RETURN_NOTOK; + } + + status = se98a_reg_read(dev, regAddress, ®Value); + if (status == RETURN_OK) { + *tempLimitValue = reg2temp(regValue); + LOGGER_DEBUG("TEMPSENSOR:INFO:: Temperature sensor 0x%x on bus " + "0x%x is having Limit configure to 0x%x.\n", + dev->cfg.dev.slave_addr, dev->cfg.dev.bus, *tempLimitValue); + } + return status; +} + +/***************************************************************************** + * Internal IRQ handler - reads in triggered interrupts and dispatches CBs + *****************************************************************************/ +static void se98a_handle_irq(void *context) { + SE98A_Dev *dev = context; + + ReturnStatus res = RETURN_NOTOK; + const IArg mutexKey = GateMutex_enter(dev->obj.mutex); { + /* See if this event was from us (we can't just read the trip status + * since those bits are sticky - they could be from an old event) */ + uint16_t config_reg; + if ((se98a_get_config_reg(dev, &config_reg) == RETURN_OK) && + (config_reg & SE98A_CFG_ESTAT)) { + /* Clear the event */ + config_reg |= SE98A_CFG_CEVENT; + res = se98a_set_config_reg(dev, config_reg); + } + } GateMutex_leave(dev->obj.mutex, mutexKey); + + if (res != RETURN_OK) { + return; + } + + /* Read the temperature register which also contains event status */ + uint16_t regValue; + if (se98a_reg_read(dev, SE98A_REG_TEMP, ®Value) != RETURN_OK) { + /* Something really strange happened */ + return; + } + + /* Grab the upper 3 bits which represent the event trip status */ + uint8_t trip_stat = (regValue >> 13) & 0x07; + int8_t temperature = reg2temp(regValue); + + /* See if we have a callback assigned to handle alerts */ + if (!dev->obj.alert_cb) { + return; + } + + /* Since > CRIT implies above window, we only handle the highest priority + * event to avoid duplicate events being sent */ + if (trip_stat & SE98A_EVT_ACT) { + dev->obj.alert_cb(SE98A_EVT_ACT, temperature, dev->obj.cb_context); + } else if (trip_stat & SE98A_EVT_AAW) { + dev->obj.alert_cb(SE98A_EVT_AAW, temperature, dev->obj.cb_context); + } else if (trip_stat & SE98A_EVT_BAW) { + dev->obj.alert_cb(SE98A_EVT_BAW, temperature, dev->obj.cb_context); + } +} + +/***************************************************************************** + *****************************************************************************/ +ReturnStatus se98a_init(SE98A_Dev *dev) +{ + dev->obj = (SE98A_Obj){}; + + dev->obj.mutex = GateMutex_create(NULL, NULL); + if (!dev->obj.mutex) { + return RETURN_NOTOK; + } + + /* Make sure we're talking to the right device */ + //if (se98a_probe(dev) != POST_DEV_FOUND) { + // return RETURN_NOTOK; + //} + + /* The only way to truly reset this device is to cycle power - we'll just + * clear out the config register to be safe and clear any interrupts from + * a previous life */ + if (se98a_set_config_reg( + dev, SE98A_CONFIG_DEFAULT | SE98A_CFG_CEVENT) != + RETURN_OK) { + return RETURN_NOTOK; + } + + if (dev->cfg.pin_evt) { + const uint32_t pin_evt_cfg = OCGPIO_CFG_INPUT | OCGPIO_CFG_INT_FALLING; + /*TODO:Temp*/ + if (OcGpio_configure(dev->cfg.pin_evt, pin_evt_cfg) < OCGPIO_SUCCESS) { + return RETURN_NOTOK; + } + + /* Use a threaded interrupt to handle IRQ */ + // ThreadedInt_Init(dev->cfg.pin_evt, se98a_handle_irq, (void *)dev); + } + return RETURN_OK; +} + +/***************************************************************************** + *****************************************************************************/ +void se98a_set_alert_handler(SE98A_Dev *dev, SE98A_CallbackFn alert_cb, + void *cb_context) +{ + dev->obj.alert_cb = alert_cb; + dev->obj.cb_context = cb_context; +} + +/***************************************************************************** + *****************************************************************************/ +ReturnStatus se98a_enable_alerts(SE98A_Dev *dev) +{ + /* Wait 125ms after setting alarm window before enabling event pin + * see datasheet page 8 - 7.3.2.1 Alarm window */ + Task_sleep(125); + + ReturnStatus res = RETURN_NOTOK; + const IArg mutexKey = GateMutex_enter(dev->obj.mutex); { + uint16_t config_reg; + if (se98a_get_config_reg(dev, &config_reg) == RETURN_OK) { + config_reg |= SE98A_CFG_EOCTL; + res = se98a_set_config_reg(dev, config_reg); + } + } GateMutex_leave(dev->obj.mutex, mutexKey); + return res; +} + +/***************************************************************************** + *****************************************************************************/ +ePostCode se98a_probe(SE98A_Dev *dev, POSTData *postData) +{ + uint8_t devId = 0x00; + uint16_t manfId = 0x0000; + if (se98a_get_dev_id(dev, &devId) != RETURN_OK) { + return POST_DEV_MISSING; + } + if (devId != SE98A_DEV_ID) { + return POST_DEV_ID_MISMATCH; + } + + if (se98a_get_mfg_id(dev, &manfId) != RETURN_OK) { + return POST_DEV_MISSING; + } + if (manfId != SE98A_MFG_ID) { + return POST_DEV_ID_MISMATCH; + } + post_update_POSTData(postData, dev->cfg.dev.bus, dev->cfg.dev.slave_addr,manfId, devId); + return POST_DEV_FOUND; +} diff --git a/firmware/src/devices/sx1509.c b/firmware/src/devices/sx1509.c new file mode 100644 index 0000000000..3746346b35 --- /dev/null +++ b/firmware/src/devices/sx1509.c @@ -0,0 +1,832 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** +#include "inc/common/byteorder.h" +#include "inc/common/global_header.h" +#include "inc/devices/sx1509.h" + +/***************************************************************************** + * REGISTER DEFINITIONS + *****************************************************************************/ +#define SX1509_REG_INPUT_DISABLE_B 0x00 /* Input buffer disable register I/O[15..8] (Bank B) */ +#define SX1509_REG_INPUT_DISABLE_A 0x01 /* Input buffer disable register I/O[7..0] (Bank A) */ +#define SX1509_REG_LONG_SLEW_B 0x02 /* Output buffer long slew register I/O[15..8] (Bank B) */ +#define SX1509_REG_LONG_SLEW_A 0x03 /* Output buffer long slew register I/O[7..0] (Bank A) */ +#define SX1509_REG_LOW_DRIVE_B 0x04 /* Output buffer low drive register I/O[15..8] (Bank B) */ +#define SX1509_REG_LOW_DRIVE_A 0x05 /* Output buffer low drive register I/O[7..0] (Bank A) */ +#define SX1509_REG_PULL_UP_B 0x06 /* Pull_up register I/O[15..8] (Bank B) */ +#define SX1509_REG_PULL_UP_A 0x07 /* Pull_up register I/O[7..0] (Bank A) */ +#define SX1509_REG_PULL_DOWN_B 0x08 /* Pull_down register I/O[15..8] (Bank B) */ +#define SX1509_REG_PULL_DOWN_A 0x09 /* Pull_down register I/O[7..0] (Bank A) */ +#define SX1509_REG_OPEN_DRAIN_B 0x0A /* Open drain register I/O[15..8] (Bank B) */ +#define SX1509_REG_OPEN_DRAIN_A 0x0B /* Open drain register I/O[7..0] (Bank A) */ +#define SX1509_REG_POLARITY_B 0x0C /* Polarity register I/O[15..8] (Bank B) */ +#define SX1509_REG_POLARITY_A 0x0D /* Polarity register I/O[7..0] (Bank A) */ +#define SX1509_REG_DIR_B 0x0E /* Direction register I/O[15..8] (Bank B) */ +#define SX1509_REG_DIR_A 0x0F /* Direction register I/O[7..0] (Bank A) */ +#define SX1509_REG_DATA_B 0x10 /* Data register I/O[15..8] (Bank B) */ +#define SX1509_REG_DATA_A 0x11 /* Data register I/O[7..0] (Bank A) */ +#define SX1509_REG_INTERRUPT_MASK_B 0x12 /* Interrupt mask register I/O[15..8] (Bank B) */ +#define SX1509_REG_INTERRUPT_MASK_A 0x13 /* Interrupt mask register I/O[7..0] (Bank A) */ +#define SX1509_REG_SENSE_HIGH_B 0x14 /* Sense register for I/O[15:12] (Bank B) */ +#define SX1509_REG_SENSE_LOW_B 0x15 /* Sense register for I/O[11:8] (Bank B) */ +#define SX1509_REG_SENSE_HIGH_A 0x16 /* Sense register for I/O[7:4] (Bank A) */ +#define SX1509_REG_SENSE_LOW_A 0x17 /* Sense register for I/O[3:0] (Bank A) */ +#define SX1509_REG_INTERRUPT_SOURCE_B 0x18 /* Interrupt source register I/O[15..8] (Bank B) */ +#define SX1509_REG_INTERRUPT_SOURCE_A 0x19 /* Interrupt source register I/O[7..0] (Bank A) */ +#define SX1509_REG_EVENT_STATUS_B 0x1A /* Event status register I/O[15..8] (Bank B) */ +#define SX1509_REG_EVENT_STATUS_A 0x1B /* Event status register I/O[7..0] (Bank A) */ +#define SX1509_REG_LEVEL_SHIFTER_1 0x1C /* Level shifter register 1 */ +#define SX1509_REG_LEVEL_SHIFTER_2 0x1D /* Level shifter register 2 */ +#define SX1509_REG_CLOCK 0x1E /* Clock management register */ +#define SX1509_REG_MISC 0x1F /* Miscellaneous device settings register */ +#define SX1509_REG_LED_DRIVER_ENABLE_B 0x20 /* LED driver enable register I/O[15..8] (Bank B) */ +#define SX1509_REG_LED_DRIVER_ENABLE_A 0x21 /* LED driver enable register I/O[7..0] (Bank A) */ +#define SX1509_REG_DEBOUNCE_CONFIG 0x22 /* Debounce configuration register */ +#define SX1509_REG_DEBOUNCE_ENABLE_B 0x23 /* Debounce enable register I/O[15..8] (Bank B) */ +#define SX1509_REG_DEBOUNCE_ENABLE_A 0x24 /* Debounce enable register I/O[7..0] (Bank A) */ +#define SX1509_REG_T_ON_0 0x29 /* ON time register for I/O[0] */ +#define SX1509_REG_I_ON_0 0x2A /* ON intensity register for I/O[0] */ +#define SX1509_REG_OFF_0 0x2B /* OFF time/intensity register for I/O[0] */ +#define SX1509_REG_T_ON_1 0x2C /* ON time register for I/O[1] */ +#define SX1509_REG_I_ON_1 0x2D /* ON intensity register for I/O[1] */ +#define SX1509_REG_OFF_1 0x2E /* OFF time/intensity register for I/O[1] */ +#define SX1509_REG_T_ON_2 0x2F /* ON time register for I/O[2] */ +#define SX1509_REG_I_ON_2 0x30 /* ON intensity register for I/O[2] */ +#define SX1509_REG_OFF_2 0x31 /* OFF time/intensity register for I/O[2] */ +#define SX1509_REG_T_ON_3 0x32 /* ON time register for I/O[3] */ +#define SX1509_REG_I_ON_3 0x33 /* ON intensity register for I/O[3] */ +#define SX1509_REG_OFF_3 0x34 /* OFF time/intensity register for I/O[3] */ +#define SX1509_REG_T_ON_4 0x35 /* ON time register for I/O[4] */ +#define SX1509_REG_I_ON_4 0x36 /* ON intensity register for I/O[4] */ +#define SX1509_REG_OFF_4 0x37 /* OFF time/intensity register for I/O[4] */ +#define SX1509_REG_T_RISE_4 0x38 /* Fade in register for I/O[4] */ +#define SX1509_REG_T_FALL_4 0x39 /* Fade out register for I/O[4] */ +#define SX1509_REG_T_ON_5 0x3A /* ON time register for I/O[5] */ +#define SX1509_REG_I_ON_5 0x3B /* ON intensity register for I/O[5] */ +#define SX1509_REG_OFF_5 0x3C /* OFF time/intensity register for I/O[5] */ +#define SX1509_REG_T_RISE_5 0x3D /* Fade in register for I/O[5] */ +#define SX1509_REG_T_FALL_5 0x3E /* Fade out register for I/O[5] */ +#define SX1509_REG_T_ON_6 0x3F /* ON time register for I/O[6] */ +#define SX1509_REG_I_ON_6 0x40 /* ON intensity register for I/O[6] */ +#define SX1509_REG_OFF_6 0x41 /* OFF time/intensity register for I/O[6] */ +#define SX1509_REG_T_RISE_6 0x42 /* Fade in register for I/O[6] */ +#define SX1509_REG_T_FALL_6 0x43 /* Fade out register for I/O[6] */ +#define SX1509_REG_T_ON_7 0x44 /* ON time register for I/O[7] */ +#define SX1509_REG_I_ON_7 0x45 /* ON intensity register for I/O[7] */ +#define SX1509_REG_OFF_7 0x46 /* OFF time/intensity register for I/O[7] */ +#define SX1509_REG_T_RISE_7 0x47 /* Fade in register for I/O[7] */ +#define SX1509_REG_T_FALL_7 0x48 /* Fade out register for I/O[7] */ +#define SX1509_REG_T_ON_8 0x49 /* ON time register for I/O[8] */ +#define SX1509_REG_I_ON_8 0x4A /* ON intensity register for I/O[8] */ +#define SX1509_REG_OFF_8 0x4B /* OFF time/intensity register for I/O[8] */ +#define SX1509_REG_T_ON_9 0x4C /* ON time register for I/O[9] */ +#define SX1509_REG_I_ON_9 0x4D /* ON intensity register for I/O[9] */ +#define SX1509_REG_OFF_9 0x4E /* OFF time/intensity register for I/O[9] */ +#define SX1509_REG_T_ON_10 0x4F /* ON time register for I/O[10] */ +#define SX1509_REG_I_ON_10 0x50 /* ON intensity register for I/O[10] */ +#define SX1509_REG_OFF_10 0x51 /* OFF time/intensity register for I/O[10] */ +#define SX1509_REG_T_ON_11 0x52 /* ON time register for I/O[11] */ +#define SX1509_REG_I_ON_11 0x53 /* ON intensity register for I/O[11] */ +#define SX1509_REG_OFF_11 0x54 /* OFF time/intensity register for I/O[11] */ +#define SX1509_REG_T_ON_12 0x55 /* ON time register for I/O[12] */ +#define SX1509_REG_I_ON_12 0x56 /* ON intensity register for I/O[12] */ +#define SX1509_REG_OFF_12 0x57 /* OFF time/intensity register for I/O[12] */ +#define SX1509_REG_T_RISE_12 0x58 /* Fade in register for I/O[12] */ +#define SX1509_REG_T_FALL_12 0x59 /* Fade out register for I/O[12] */ +#define SX1509_REG_T_ON_13 0x5A /* ON time register for I/O[13] */ +#define SX1509_REG_I_ON_13 0x5B /* ON intensity register for I/O[13] */ +#define SX1509_REG_OFF_13 0x5C /* OFF time/intensity register for I/O[13] */ +#define SX1509_REG_T_RISE_13 0x5D /* Fade in register for I/O[13] */ +#define SX1509_REG_T_FALL_13 0x5E /* Fade out register for I/O[13] */ +#define SX1509_REG_T_ON_14 0x5F /* ON time register for I/O[14] */ +#define SX1509_REG_I_ON_14 0x60 /* ON intensity register for I/O[14] */ +#define SX1509_REG_OFF_14 0x61 /* OFF time/intensity register for I/O[14] */ +#define SX1509_REG_T_RISE_14 0x62 /* Fade in register for I/O[14] */ +#define SX1509_REG_T_FALL_14 0x63 /* Fade out register for I/O[14] */ +#define SX1509_REG_T_ON_15 0x64 /* ON time register for I/O[15] */ +#define SX1509_REG_I_ON_15 0x65 /* ON intensity register for I/O[15] */ +#define SX1509_REG_OFF_15 0x66 /* OFF time/intensity register for I/O[15] */ +#define SX1509_REG_T_RISE_15 0x67 /* Fade in register for I/O[15] */ +#define SX1509_REG_T_FALL_15 0x68 /* Fade out register for I/O[15] */ +#define SX1509_REG_HIGH_INPUT_B 0x69 /* High input enable register I/O[15..8] (Bank B) */ +#define SX1509_REG_HIGH_INPUT_A 0x6A /* High input enable register I/O[7..0] (Bank A) */ +#define SX1509_REG_RESET 0x7D /* Software reset register */ +#define SX1509_REG_TEST_1 0x7E /* Test register 1 */ +#define SX1509_REG_TEST_2 0x7F /* Test register 2 */ + +/* Values being used for Soft reset of SX1509 */ +#define SX1509_SOFT_RESET_REG_VALUE_1 0x12 +#define SX1509_SOFT_RESET_REG_VALUE_2 0x34 + +static uint8_t SX1509_REG_T_ON[16] = { + SX1509_REG_T_ON_0, SX1509_REG_T_ON_1, SX1509_REG_T_ON_2, SX1509_REG_T_ON_3, + SX1509_REG_T_ON_4, SX1509_REG_T_ON_5, SX1509_REG_T_ON_6, SX1509_REG_T_ON_7, + SX1509_REG_T_ON_8, SX1509_REG_T_ON_9, SX1509_REG_T_ON_10, SX1509_REG_T_ON_11, + SX1509_REG_T_ON_12, SX1509_REG_T_ON_13, SX1509_REG_T_ON_14, SX1509_REG_T_ON_15 +}; + +static uint8_t SX1509_REG_OFF[16] = { + SX1509_REG_OFF_0, SX1509_REG_OFF_1, SX1509_REG_OFF_2, SX1509_REG_OFF_3, + SX1509_REG_OFF_4, SX1509_REG_OFF_5, SX1509_REG_OFF_6, SX1509_REG_OFF_7, + SX1509_REG_OFF_8, SX1509_REG_OFF_9, SX1509_REG_OFF_10, SX1509_REG_OFF_11, + SX1509_REG_OFF_12, SX1509_REG_OFF_13, SX1509_REG_OFF_14, SX1509_REG_OFF_15 +}; + +#if 0 +static uint8_t SX1509_REG_I_ON[16] = { + SX1509_REG_I_ON_0, SX1509_REG_I_ON_1, SX1509_REG_I_ON_2, SX1509_REG_I_ON_3, + SX1509_REG_I_ON_4, SX1509_REG_I_ON_5, SX1509_REG_I_ON_6, SX1509_REG_I_ON_7, + SX1509_REG_I_ON_8, SX1509_REG_I_ON_9, SX1509_REG_I_ON_10, SX1509_REG_I_ON_11, + SX1509_REG_I_ON_12, SX1509_REG_I_ON_13, SX1509_REG_I_ON_14, SX1509_REG_I_ON_15 }; +#endif + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_raw_read + ** + ** DESCRIPTION : Read the register value from IO Expander with LED + ** driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register address and pointer + ** to value read. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus ioexp_led_raw_read(const I2C_Dev *i2c_dev, + uint8_t regAddress, + uint8_t *regValue) +{ + ReturnStatus status = RETURN_NOTOK; + I2C_Handle sx1509_handle = i2c_get_handle(i2c_dev->bus); + uint16_t value = 0x0000; + if (!sx1509_handle) { + LOGGER_ERROR("SX1509:ERROR:: Failed to get I2C Bus for SX1509 0x%02x " + "on bus 0x%02x.\n", i2c_dev->slave_addr, i2c_dev->bus); + } else { + status = i2c_reg_read(sx1509_handle, i2c_dev->slave_addr, regAddress, + &value, 1); + if (status == RETURN_OK) { + *regValue = (uint8_t)(value); + } + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_raw_write + ** + ** DESCRIPTION : Write the register value(s) to IO Expander with LED + ** driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register address, value 1 + ** & value 2 to be written and No of bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +static ReturnStatus ioexp_led_raw_write(const I2C_Dev *i2c_dev, + uint8_t regAddress, + uint8_t regValue1, + uint8_t regValue2, + uint8_t noOfBytes) +{ + ReturnStatus status = RETURN_NOTOK; + I2C_Handle sx1509_handle = i2c_get_handle(i2c_dev->bus); + uint16_t value = 0x00; + if (noOfBytes == 2) { + value = (regValue2<<8) | (regValue1); + value = htobe16(value); + } else { + value = regValue1; + } + if (!sx1509_handle) { + LOGGER_ERROR("SX1509:ERROR:: Failed to get I2C Bus for SX1509 0x%02x " + "on bus 0x%02x.\n", i2c_dev->slave_addr, i2c_dev->bus); + } else { + status = i2c_reg_write(sx1509_handle, i2c_dev->slave_addr, regAddress, + value, noOfBytes); + } +return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_get_data + ** + ** DESCRIPTION : Read the Data Register Value from IO Expander with LED + ** driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type and pointer + ** to value read. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_get_data(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t *regValue) +{ + ReturnStatus status = RETURN_OK; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_DATA_A) : (SX1509_REG_DATA_B); + status = ioexp_led_raw_read(i2c_dev, regAddress, regValue); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_set_data + ** + ** DESCRIPTION : Write the Data Register Value(s) into IO Expander with + ** LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, value 1 & + ** value 2 to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_set_data(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t regValue1, + uint8_t regValue2) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_DATA_A) : (SX1509_REG_DATA_B); + if (regType == SX1509_REG_AB) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, regValue1, + regValue2, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_set_on_time + ** + ** DESCRIPTION : Write the ON Time Register Value into IO Expander + ** with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, index to On time Register + ** and value to be written. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_set_on_time(const I2C_Dev *i2c_dev, uint8_t index, + uint8_t tOnRegValue) +{ + ReturnStatus status = RETURN_OK; + status = ioexp_led_raw_write(i2c_dev, SX1509_REG_T_ON[index], + tOnRegValue, 0, 1); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_set_off_time + ** + ** DESCRIPTION : Write the OFF Time Register Value into IO Expander + ** with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, index to Off time Register + ** and value to be written. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_set_off_time(const I2C_Dev *i2c_dev, uint8_t index, + uint8_t tOffRegValue) +{ + ReturnStatus status = RETURN_OK; + status = ioexp_led_raw_write(i2c_dev, SX1509_REG_OFF[index], + tOffRegValue, 0, 1); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_software_reset + ** + ** DESCRIPTION : Do LED SX159 Soft Reset by writing 0x12 followed by + ** 0x34 on software reset register. + ** + ** ARGUMENTS : Subsystem and Slave address. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_software_reset(const I2C_Dev *i2c_dev) +{ + ReturnStatus status = RETURN_OK; + + status = ioexp_led_raw_write(i2c_dev, SX1509_REG_RESET, + SX1509_SOFT_RESET_REG_VALUE_1, 0, 1); + if (status == RETURN_OK) { + + status = ioexp_led_raw_write(i2c_dev, SX1509_REG_RESET, + SX1509_SOFT_RESET_REG_VALUE_2, 0, 1); + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_inputbuffer + ** + ** DESCRIPTION : Write the Input Diable Input Buffer Register Value(s) + ** into IO Expander with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, value 1 & + ** value 2 to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_config_inputbuffer(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t inputBuffRegValue1, + uint8_t inputBuffRegValue2) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_INPUT_DISABLE_A) : (SX1509_REG_INPUT_DISABLE_B); + if (regType == SX1509_REG_AB) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, inputBuffRegValue1, + inputBuffRegValue2, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_pullup + ** + ** DESCRIPTION : Write the Pull_up Register Value(s) into IO Expander + ** with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, value 1 & + ** value 2 to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_config_pullup(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t pullUpRegValue1, + uint8_t pullUpRegValue2) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_PULL_UP_A) : (SX1509_REG_PULL_UP_B); + if (regType == SX1509_REG_AB) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, pullUpRegValue1, + pullUpRegValue2, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_pulldown + ** + ** DESCRIPTION : Write the Pull Down Register Value(s) into IO Expander + ** with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, value 1 & + ** value 2 to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_config_pulldown(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t pullDownRegValue1, + uint8_t pullDownRegValue2) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_PULL_DOWN_A) : (SX1509_REG_PULL_DOWN_B); + if (regType == SX1509_REG_AB) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, pullDownRegValue1, + pullDownRegValue2, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_opendrain + ** + ** DESCRIPTION : Write the Open drain Register Value(s) into IO Expander + ** with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, value 1 & + ** value 2 to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_config_opendrain(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t openDrainRegValue1, + uint8_t openDrainRegValue2) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_OPEN_DRAIN_A) : (SX1509_REG_OPEN_DRAIN_B); + if (regType == SX1509_REG_AB) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, openDrainRegValue1, + openDrainRegValue2, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_data_direction + ** + ** DESCRIPTION : Write the Direction Register Value(s) into IO Expander + ** with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, value 1 & + ** value 2 to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_config_data_direction(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t directionRegValue1, + uint8_t directionRegValue2) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_DIR_A) : (SX1509_REG_DIR_B); + if (regType == SX1509_REG_AB) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, directionRegValue1, + directionRegValue2, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_polarity + ** + ** DESCRIPTION : Write the Polarity Register Value(s) into IO Expander + ** with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, value 1 & + ** value 2 to be written. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_config_polarity(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t polarityRegValue1, + uint8_t polarityRegValue2) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_POLARITY_A) : (SX1509_REG_POLARITY_B); + if (regType == SX1509_REG_AB) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, polarityRegValue1, + polarityRegValue2, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_clock + ** + ** DESCRIPTION : Write the Clock management Register Value into IO + ** Expander with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address and value to be written. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +/* RegClock: + * 6:5 - Oscillator frequency souce + * 00: off, 01: external input, 10: internal 2MHz, 11: reserved + * 4 - OSCIO pin function + * 0: input, 1 ouptut + * 3:0 - Frequency of oscout pin + * 0: LOW, 0xF: high, else fOSCOUT = FoSC/(2^(RegClock[3:0]-1)) + */ +ReturnStatus ioexp_led_config_clock(const I2C_Dev *i2c_dev, uint8_t oscSource, + uint8_t oscPin) +{ + ReturnStatus status = RETURN_OK; + uint8_t regValue = 0; + + regValue = oscSource << 5; + regValue |= oscPin << 4; + + status = ioexp_led_raw_write(i2c_dev, SX1509_REG_CLOCK, regValue, 0, 1); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_misc + ** + ** DESCRIPTION : Write the Miscellaneous device settings Register Value + ** into IO Expander with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address and value to be written. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_config_misc(const I2C_Dev *i2c_dev, uint8_t regValue) +{ + ReturnStatus status = RETURN_OK; + status = ioexp_led_raw_write(i2c_dev, SX1509_REG_MISC, regValue, 0, 1); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_enable_leddriver + ** + ** DESCRIPTION : Write the LED driver enable Value(s) into IO Expander + ** with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, value 1 & + ** value 2 to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_enable_leddriver(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t ledEnableRegValue1, + uint8_t ledEnableRegValue2) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_LED_DRIVER_ENABLE_A) : + (SX1509_REG_LED_DRIVER_ENABLE_B); + if (regType == SX1509_REG_AB) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, ledEnableRegValue1, + ledEnableRegValue2, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_read_testregister_1 + ** + ** DESCRIPTION : Read the Test Register Value from IO Expander with LED + ** driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address and pointer to value read. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_read_testregister_1(const I2C_Dev *i2c_dev, + uint8_t *regValue) +{ + ReturnStatus status = RETURN_OK; + status = ioexp_led_raw_read(i2c_dev, SX1509_REG_TEST_1, regValue); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_interrupt + ** + ** DESCRIPTION : Write the Interrupt Mask Register Value(s) into IO + ** Expander with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, value 1 & + ** value 2 to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_config_interrupt(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t interruptMaskRegValue1, + uint8_t interruptMaskRegValue2) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_INTERRUPT_MASK_A) : + (SX1509_REG_INTERRUPT_MASK_B); + if (regType == SX1509_REG_AB) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, interruptMaskRegValue1, + interruptMaskRegValue2, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_edge_sense_A + ** + ** DESCRIPTION : Write the Edge Sense Register A(I/O[7:0]) Value(s) + ** into IO Expander with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, Low byte value + ** & High byte values of A to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +/* + * 00 : None + * 01 : Rising + * 10 : Falling + * 11 : Both + */ +ReturnStatus ioexp_led_config_edge_sense_A(const I2C_Dev *i2c_dev, + sx1509EdgeSenseRegType regType, + uint8_t edgeSenseLowARegValue, + uint8_t edgeSenseHighARegValue) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_EDGE_SENSE_REG_LOW) ? + (SX1509_REG_SENSE_LOW_A) : (SX1509_REG_SENSE_HIGH_A); + if (regType == SX1509_EDGE_SENSE_REG_LOW_HIGH) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, edgeSenseLowARegValue, + edgeSenseHighARegValue, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_edge_sense_B + ** + ** DESCRIPTION : Write the Edge Sense Register B(I/O[15:8]) Value(s) + ** into IO Expander with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, Low byte value + ** & High byte values of B to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_config_edge_sense_B(const I2C_Dev *i2c_dev, + sx1509EdgeSenseRegType regType, + uint8_t edgeSenseLowBRegValue, + uint8_t edgeSenseHighBRegValue) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_EDGE_SENSE_REG_LOW) ? + (SX1509_REG_SENSE_LOW_B) : (SX1509_REG_SENSE_HIGH_B); + if (regType == SX1509_EDGE_SENSE_REG_LOW_HIGH) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, edgeSenseLowBRegValue, + edgeSenseHighBRegValue, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_config_debounce + ** + ** DESCRIPTION : Write the Debounce time Value into IO Expander with + ** LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address and debounce Time(ms) to be + ** written. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +/* Debounce time-to-byte map: (assuming fOsc = 2MHz; 2^(n-1)) + * 0: 0.5ms 1: 1ms + * 2: 2ms 3: 4ms + * 4: 8ms 5: 16ms + * 6: 32ms 7: 64ms + */ +ReturnStatus ioexp_led_config_debounce_time(const I2C_Dev *i2c_dev, + uint8_t debounceTime) +{ + ReturnStatus status = RETURN_OK; + uint8_t index = 0; + uint8_t regValue = 0; + + for (index = 0; index < 8; index++) { + if (debounceTime & (1 << index)) { + regValue = index + 1; + break; + } + } + + status = ioexp_led_raw_write(i2c_dev, SX1509_REG_DEBOUNCE_CONFIG, + regValue, 0, 1); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_enable_debounce + ** + ** DESCRIPTION : Write the Debounce enable Register Value into + ** IO Expander with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type, value 1 & + ** value 2 to be written and No of Bytes. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_enable_debounce(const I2C_Dev *i2c_dev, + sx1509RegType regType, + uint8_t debounceEnableRegValue1, + uint8_t debounceEnableRegValue2) +{ + ReturnStatus status = RETURN_OK; + uint8_t noOfBytes = 1; + uint8_t regAddress = (regType == SX1509_REG_A) ? + (SX1509_REG_DEBOUNCE_ENABLE_A) : (SX1509_REG_DEBOUNCE_ENABLE_B); + if (regType == SX1509_REG_AB) { + noOfBytes = 2; + } + + status = ioexp_led_raw_write(i2c_dev, regAddress, debounceEnableRegValue1, + debounceEnableRegValue2, noOfBytes); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_read_interrupt_source + ** + ** DESCRIPTION : Read the Interrupt Source Register Value from IO + ** Expander with LED driver SX1509. + ** + ** ARGUMENTS : Subsystem, Slave address, Register Type and pointer + ** to value read. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_get_interrupt_source(const I2C_Dev *i2c_dev, + uint16_t *intPins) +{ + ReturnStatus status = RETURN_OK; + uint8_t regValueA = 0; + uint8_t regValueB = 0; + + status = ioexp_led_raw_read(i2c_dev, SX1509_REG_INTERRUPT_SOURCE_A, + ®ValueA); + if (status != RETURN_OK) { + return status; + } + status = ioexp_led_raw_read(i2c_dev, SX1509_REG_INTERRUPT_SOURCE_B, + ®ValueB); + *intPins = (uint16_t) ((regValueB << 8) | regValueA); + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : ioexp_led_clear_interrupt_source + ** + ** DESCRIPTION : Clear the Interrupt status. + ** + ** ARGUMENTS : Subsystem and Slave address. + ** + ** RETURN TYPE : Success or failure + ** + *****************************************************************************/ +ReturnStatus ioexp_led_clear_interrupt_source(const I2C_Dev *i2c_dev) +{ + ReturnStatus status = RETURN_OK; + status = ioexp_led_raw_write(i2c_dev, SX1509_REG_INTERRUPT_SOURCE_B, + 0xFF, 0xFF, 2); + return status; +} diff --git a/firmware/src/drivers/GpioNative.c b/firmware/src/drivers/GpioNative.c new file mode 100644 index 0000000000..81ca161b50 --- /dev/null +++ b/firmware/src/drivers/GpioNative.c @@ -0,0 +1,273 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#include "GpioNative.h" + +#include "common/inc/global/OC_CONNECT1.h" +#include "inc/common/global_header.h" + +#include +#include + +#include + +static GateMutex_Handle s_cb_data_mutex; + +/***************************************************************************** + ** FUNCTION NAME : GpioNative_probe + ** + ** DESCRIPTION : probe function called as part of POST + ** + ** ARGUMENTS : none + ** + ** RETURN TYPE : OCGPIO_SUCCESS or OCGPIO_FAILURE + ** + *****************************************************************************/ +static int GpioNative_probe(void) { + //This probe function is just a dummy as we are all ready accessing EC. + return OCGPIO_SUCCESS; +} +/***************************************************************************** + ** FUNCTION NAME : GpioNative_init + ** + ** DESCRIPTION : inits as part of system bootup + ** + ** ARGUMENTS : NONE + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void GpioNative_init(void) { + s_cb_data_mutex = GateMutex_create(NULL, NULL); +} + +/***************************************************************************** + ** FUNCTION NAME : GpioNative_write + ** + ** DESCRIPTION : gpio write for the required pin and value + ** + ** ARGUMENTS : pin index , value + ** + ** RETURN TYPE : OCGPIO_SUCCESS or OCGPIO_FAILURE + ** + *****************************************************************************/ +static int GpioNative_write(const OcGpio_Pin *pin, bool value) { + if (pin->hw_cfg & OCGPIO_CFG_INVERT) { + value = !value; + } + GPIO_write(pin->idx, value); + return OCGPIO_SUCCESS; +} + +/***************************************************************************** + ** FUNCTION NAME : GpioNative_read + ** + ** DESCRIPTION : does a gpio read for the provided pin + ** + ** ARGUMENTS : pin index + ** + ** RETURN TYPE : int + ** + *****************************************************************************/ +static int GpioNative_read(const OcGpio_Pin *pin) { + bool value = GPIO_read(pin->idx); + if (pin->hw_cfg & OCGPIO_CFG_INVERT) { + value = !value; + } + return value; +} + +/***************************************************************************** + ** FUNCTION NAME : GetBank + ** + ** DESCRIPTION : configure the pins with the provided config + ** + ** ARGUMENTS : pointer to pin, config + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioNative_configure(const OcGpio_Pin *pin, uint32_t cfg) { + /* TODO: translate config values to account for inversion + * eg. If inverted, change rising edge trigger to falling edge + */ + /* TODO: need to be careful in multi-subscriber case - if we reconfigure, + * the TI driver clears any interrupts on this pin, so we could potentially + * miss edges for already subscribed pins + */ + const OcGpio_HwCfg hw_cfg = { .uint16 = pin->hw_cfg }; + const OcGpio_ioCfg io_cfg = { .uint32 = cfg }; + uint32_t ti_cfg = 0x00; + + if (cfg & OCGPIO_CFG_INPUT) { + ti_cfg |= GPIO_CFG_INPUT; + ti_cfg |= (hw_cfg.in_cfg << GPIO_CFG_IO_LSB); /* Include PU/PD cfg */ + + /* Process interrupt config (respect inversion settings) */ + if (hw_cfg.invert) { + switch ((uint32_t)io_cfg.int_cfg << GPIO_CFG_INT_LSB) { + case GPIO_CFG_IN_INT_FALLING: + ti_cfg |= GPIO_CFG_IN_INT_RISING; + break; + case GPIO_CFG_IN_INT_RISING: + ti_cfg |= GPIO_CFG_IN_INT_FALLING; + break; + case GPIO_CFG_IN_INT_LOW: + ti_cfg |= GPIO_CFG_IN_INT_HIGH; + break; + case GPIO_CFG_IN_INT_HIGH: + ti_cfg |= GPIO_CFG_IN_INT_LOW; + break; + + case OCGPIO_CFG_INT_NONE: + case GPIO_CFG_IN_INT_BOTH_EDGES: + default: + /* No change */ + ti_cfg |= (io_cfg.int_cfg << GPIO_CFG_INT_LSB); + break; + } + } else { + ti_cfg |= (io_cfg.int_cfg << GPIO_CFG_INT_LSB); + } + } else { + ti_cfg |= GPIO_CFG_OUTPUT; + ti_cfg |= (hw_cfg.out_cfg << GPIO_CFG_IO_LSB); /* Include od/pu/pd cfg */ + ti_cfg |= (hw_cfg.out_str << GPIO_CFG_OUT_STRENGTH_LSB); + ti_cfg |= (io_cfg.default_val << GPIO_CFG_OUT_BIT); + } + GPIO_setConfig(pin->idx, ti_cfg); + return OCGPIO_SUCCESS; +} + +/* TODO: since every GPIO driver will probably need this, might be worth + * moving out to GPIO I/F module + */ +typedef struct GpioCallbackData { + const OcGpio_Pin *pin; + OcGpio_CallbackFn callback; + void *context; + struct GpioCallbackData *next; /*!< Pointer to next pin subscriber */ +} GpioCallbackData; + +/* This allows us to work around the native GPIO driver's poor design and allow + * a context pointer to be passed to the calling function + */ +static GpioCallbackData *cb_data[OC_EC_GPIOCOUNT]; + +/* + */ +/***************************************************************************** + ** FUNCTION NAME : _nativeCallback + ** + ** DESCRIPTION : Wrapper to allow us to map TI-GPIO callback to all + ** our subscribers (with context passing) + ** + ** ARGUMENTS : pin index + ** + ** RETURN TYPE : NONE + ** + *****************************************************************************/ +static void _nativeCallback(unsigned int idx) { + GpioCallbackData *cbData = cb_data[idx]; + while (cbData) { + cbData->callback(cbData->pin, cbData->context); + cbData = cbData->next; + } + return; +} + +/***************************************************************************** + ** FUNCTION NAME : GpioNative_setCallback + ** + ** DESCRIPTION : set call back function for handling interrupts. + ** + ** ARGUMENTS : pointer to pin , call back function pointer , context + ** to be passed as argument to the call back function + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioNative_setCallback(const OcGpio_Pin *pin, + OcGpio_CallbackFn callback, + void *context) { + /* TODO: we may want to support callback removal at some point */ + if (!callback) { + return OCGPIO_FAILURE; + } + + GpioCallbackData *cb_entry = malloc(sizeof(GpioCallbackData)); + if (!cb_entry) { + LOGGER_ERROR("Unable to malloc GPIO callback"); + return OCGPIO_FAILURE; + } + + *cb_entry = (GpioCallbackData){ + .pin = pin, + .callback = callback, + .context = context, + }; + + /* find next blank entry & assign new callback data (protect against + * multiple tasks accessing simultaneously) + * Note: we don't need to worry about the actual callback function using + * a mutex, since the 'next' pointer assignment is atomic */ + const IArg mutexKey = GateMutex_enter(s_cb_data_mutex); + { + GpioCallbackData **next = &cb_data[pin->idx]; + while (*next) { + next = &(*next)->next; + } + *next = cb_entry; + } + GateMutex_leave(s_cb_data_mutex, mutexKey); + GPIO_setCallback(pin->idx, _nativeCallback); + return OCGPIO_SUCCESS; +} + +/* TODO: what if multiple tasks are sharing a pin and one of them + * disables the interrupt? */ +/***************************************************************************** + ** FUNCTION NAME : GpioNative_disableInt + ** + ** DESCRIPTION : identify the bank. + ** + ** ARGUMENTS : pin index + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioNative_disableInt(const OcGpio_Pin *pin) { + GPIO_disableInt(pin->idx); + return OCGPIO_SUCCESS; +} + +/***************************************************************************** + ** FUNCTION NAME : GpioNative_enableInt + ** + ** DESCRIPTION : enable interrupt for a particular pin + ** + ** ARGUMENTS : pin index + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioNative_enableInt(const OcGpio_Pin *pin) { + GPIO_enableInt(pin->idx); + return OCGPIO_SUCCESS; +} + +const OcGpio_FnTable GpioNative_fnTable = { + .probe = GpioNative_probe, + .write = GpioNative_write, + .read = GpioNative_read, + .configure = GpioNative_configure, + .setCallback = GpioNative_setCallback, + .disableInt = GpioNative_disableInt, + .enableInt = GpioNative_enableInt, +}; diff --git a/firmware/src/drivers/GpioNative.h b/firmware/src/drivers/GpioNative.h new file mode 100644 index 0000000000..68ec2711e2 --- /dev/null +++ b/firmware/src/drivers/GpioNative.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#ifndef _GPIONATIVE_H_ +#define _GPIONATIVE_H_ + +#include "OcGpio.h" + +extern const OcGpio_FnTable GpioNative_fnTable; + +/* + * Must be called before using the GPIO Native driver + */ +void GpioNative_init(void); + +#endif /* _GPIONATIVE_H_ */ diff --git a/firmware/src/drivers/GpioSX1509.c b/firmware/src/drivers/GpioSX1509.c new file mode 100644 index 0000000000..9f1ffa5fed --- /dev/null +++ b/firmware/src/drivers/GpioSX1509.c @@ -0,0 +1,503 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#include "devices/i2c/threaded_int.h" +#include "GpioSX1509.h" +#include "helpers/memory.h" +#include "inc/common/global_header.h" +#include "inc/devices/sx1509.h" + +#include +#include + +/***************************************************************************** + ** FUNCTION NAME : GetBank + ** + ** DESCRIPTION : identify the bank. + ** + ** ARGUMENTS : pin index + ** + ** RETURN TYPE : SX1509_REG_B or SX1509_REG_A + ** + *****************************************************************************/ +static sx1509RegType GetBank(uint8_t pin_idx) { + return (pin_idx > 7) ? SX1509_REG_B : SX1509_REG_A; +} + +/***************************************************************************** + ** FUNCTION NAME : RelativePinIdx + ** + ** DESCRIPTION : identify the relative pin index. + ** + ** ARGUMENTS : pin idx + ** + ** RETURN TYPE : uint8_t + ** + *****************************************************************************/ +static uint8_t RelativePinIdx(uint8_t pin_idx) { + return (pin_idx > 7) ? (pin_idx - 8) : pin_idx; +} + +/***************************************************************************** + ** FUNCTION NAME : HandleIRQ + ** + ** DESCRIPTION : IRQ handler - reads in triggered interrupts and + ** dispatches + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : none + ** + *****************************************************************************/ +static void HandleIRQ(void *context) { + const OcGpio_Port *port = context; + const SX1509_Cfg *sx_cfg = port->cfg; + SX1509_Obj *obj = port->object_data; + + /* Figure out which pin this interrupt came from */ + /* TODO: this seems risky - what if an interrupt occurs between reading + * src and clearing it? It's dumb that the IC doesn't clear the irq on + * reading the source */ + uint16_t irq_src; + ioexp_led_get_interrupt_source(&sx_cfg->i2c_dev, &irq_src); + ioexp_led_clear_interrupt_source(&sx_cfg->i2c_dev); + + // Dispatch the interrupt handlers + int pin_idx = 0; + while (irq_src) { + if (irq_src & 0x01) { + SX1509CallbackData *cbData = obj->cb_data[pin_idx]; + while (cbData) { + cbData->callback(cbData->pin, cbData->context); + cbData = cbData->next; + } + } + ++pin_idx; + irq_src >>= 1; + } +} + +/***************************************************************************** + ** FUNCTION NAME : GpioSX1509_probe + ** + ** DESCRIPTION : post related api which check connected to the device. + ** + ** ARGUMENTS : pointer to port + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioSX1509_probe(const OcGpio_Port *port) { + /* if we are able to read configuration register this means PCA device is accessible*/ + const SX1509_Cfg *sx_cfg = port->cfg; + SX1509_Obj *obj = port->object_data; + uint8_t input_reg; + if (ioexp_led_get_data(&sx_cfg->i2c_dev, 0, &input_reg) != RETURN_OK) { + return OCGPIO_FAILURE; + } + return OCGPIO_SUCCESS; +} + +/***************************************************************************** + ** FUNCTION NAME : GpioSX1509_init + ** + ** DESCRIPTION : init . + ** + ** ARGUMENTS : pointer to the port + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioSX1509_init(const OcGpio_Port *port) { + const SX1509_Cfg *sx_cfg = port->cfg; + SX1509_Obj *obj = port->object_data; + + obj->mutex = GateMutex_create(NULL, NULL); + for (int i = 0; i < SX1509_NUM_BANKS; ++i) { + obj->regs[i] = (SX1509_Registers){ + /* We only need to set the non-zero registers */ + .direction = 0xff, + .data = 0xff, + .int_mask = 0xff, + }; + } + + memset(obj->cb_data, 0, sizeof(obj->cb_data)); + + /* Make sure the IC is set to default config */ + if (ioexp_led_software_reset(&sx_cfg->i2c_dev) + != RETURN_OK) { + return OCGPIO_FAILURE; + } + + /* Register the SX1509's own IRQ pin (optional) */ + if (sx_cfg->pin_irq) { + const uint32_t pin_irq_cfg = OCGPIO_CFG_INPUT | OCGPIO_CFG_INT_FALLING; + if (OcGpio_configure(sx_cfg->pin_irq, pin_irq_cfg) < OCGPIO_SUCCESS) { + return OCGPIO_FAILURE; + } + + /* Use a threaded interrupt to handle IRQ */ + // ThreadedInt_Init(sx_cfg->pin_irq, HandleIRQ, (void *)port); + } + return OCGPIO_SUCCESS; +} + +/***************************************************************************** + ** FUNCTION NAME : GpioSX1509_write + ** + ** DESCRIPTION : write into a particular pin + ** + ** ARGUMENTS : pointer to pin, value + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioSX1509_write(const OcGpio_Pin *pin, bool value) { + const SX1509_Cfg *sx_cfg = pin->port->cfg; + SX1509_Obj *obj = pin->port->object_data; + int res = OCGPIO_FAILURE; + + const IArg mutexKey = GateMutex_enter(obj->mutex); + { + /* Reading the value of output pins seems unreliable on the SX1509, + * so we cache the output value instead (it's faster anyway) + */ + const sx1509RegType bank = GetBank(pin->idx); + const uint8_t pin_idx = RelativePinIdx(pin->idx); + const uint8_t new_reg_value = set_bit8(obj->regs[bank].data, + pin_idx, value); + if (ioexp_led_set_data(&sx_cfg->i2c_dev, bank, new_reg_value, 0x00) + != RETURN_OK) { + goto cleanup; + } + obj->regs[bank].data = new_reg_value; + res = OCGPIO_SUCCESS; + } +cleanup: + GateMutex_leave(obj->mutex, mutexKey); + return res; +} + +/***************************************************************************** + ** FUNCTION NAME : GpioSX1509_read + ** + ** DESCRIPTION : read a particular pin + ** + ** ARGUMENTS : pointer to pin + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioSX1509_read(const OcGpio_Pin *pin) { + const SX1509_Cfg *sx_cfg = pin->port->cfg; + + /* We don't need a mutex here since i2c driver protects against + * simultaneous access and we're just reading a value */ + const sx1509RegType bank = GetBank(pin->idx); + const uint8_t pin_idx = RelativePinIdx(pin->idx); + uint8_t input_reg; + if (ioexp_led_get_data(&sx_cfg->i2c_dev, bank, &input_reg) != RETURN_OK) { + return OCGPIO_FAILURE; + } + + return (input_reg >> pin_idx) & 0x01; +} + +/* TODO: this mapping is pretty gross with the shifts */ +static const uint8_t EDGE_SENSE_MAP[] = { + [OCGPIO_CFG_INT_NONE >> OCGPIO_CFG_INT_LSB] = 0x00, /* 00 */ + [OCGPIO_CFG_INT_RISING >> OCGPIO_CFG_INT_LSB] = 0x01, /* 01 */ + [OCGPIO_CFG_INT_FALLING >> OCGPIO_CFG_INT_LSB] = 0x02, /* 10 */ + [OCGPIO_CFG_INT_BOTH_EDGES >> OCGPIO_CFG_INT_LSB] = 0x03, /* 11 */ +}; + +/* TODO: handle things nicely if we get a failure part way through config - + * right now we break the rule of the other functions to only store the new + * value if the IC accepted the changed value */ +/***************************************************************************** + ** FUNCTION NAME : GpioSX1509_configure + ** + ** DESCRIPTION : configure the pins with the provided config + ** + ** ARGUMENTS : pointer to pin, sx1509 config + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioSX1509_configure(const OcGpio_Pin *pin, uint32_t cfg) { + const SX1509_Cfg *sx_cfg = pin->port->cfg; + SX1509_Obj *obj = pin->port->object_data; + int res = OCGPIO_FAILURE; + + const OcGpio_ioCfg io_cfg = { .uint32 = cfg }; + + const sx1509RegType bank = GetBank(pin->idx); + const uint8_t pin_idx = RelativePinIdx(pin->idx); + SX1509_Registers *reg = &obj->regs[bank]; + + const IArg mutexKey = GateMutex_enter(obj->mutex); + { + /* Invert the polarity (1) if necessary */ + reg->polarity = set_bit8(reg->polarity, pin_idx, + pin->hw_cfg & OCGPIO_CFG_INVERT); + if (ioexp_led_config_polarity(&sx_cfg->i2c_dev, bank, reg->polarity, + 0x00) != RETURN_OK) { + goto cleanup; + } + + bool pu_en; + bool pd_en; + + /* Set pull-up/down registers */ + /* Set output-specific registers if applicable */ + if (io_cfg.dir == OCGPIO_CFG_OUTPUT) { + /* Enable (1) open drain */ + const bool od_en = (pin->hw_cfg & OCGPIO_CFG_OUT_OD_MASK); + reg->open_drain = set_bit8(reg->open_drain, pin_idx, od_en); + if (ioexp_led_config_opendrain(&sx_cfg->i2c_dev, + bank, + reg->open_drain, + 0x00) != RETURN_OK) { + goto cleanup; + } + + /* Disable (1) the input buffer */ + reg->input_buf_disable = set_bit8(reg->input_buf_disable, pin_idx, + 1); + if (ioexp_led_config_inputbuffer(&sx_cfg->i2c_dev, + bank, + reg->input_buf_disable, + 0x00) != RETURN_OK) { + goto cleanup; + } + + /* Set default value */ + GpioSX1509_write(pin, io_cfg.default_val); + + /* TODO: this is kind of gross, not sure if it's worth keeping + * compatibility with TI-GPIO cfg */ + pu_en = ((pin->hw_cfg & OCGPIO_CFG_OUT_OD_MASK) == + OCGPIO_CFG_OUT_OD_PU); + pd_en = ((pin->hw_cfg & OCGPIO_CFG_OUT_OD_MASK) == + OCGPIO_CFG_OUT_OD_PD); + } else { + /* Enable (0) the input buffer */ + reg->input_buf_disable = set_bit8(reg->input_buf_disable, pin_idx, + 0); + if (ioexp_led_config_inputbuffer(&sx_cfg->i2c_dev, + bank, + reg->input_buf_disable, + 0x00) != RETURN_OK) { + goto cleanup; + } + + /* Set interrupt edge detection */ + /* This is a bit tricky since we need to set a pair of bits */ + const uint16_t EDGE_SENSE_MASK = 0x03 << (pin_idx * 2); + reg->edge_sense &= ~EDGE_SENSE_MASK; + reg->edge_sense |= EDGE_SENSE_MAP[io_cfg.int_cfg] << (pin_idx * 2); + + switch (bank) { + case SX1509_REG_A: + if (ioexp_led_config_edge_sense_A( + &sx_cfg->i2c_dev, + SX1509_EDGE_SENSE_REG_LOW_HIGH, + LOBYTE(reg->edge_sense), + HIBYTE(reg->edge_sense)) != RETURN_OK) { + goto cleanup; + } + break; + case SX1509_REG_B: + if (ioexp_led_config_edge_sense_B( + &sx_cfg->i2c_dev, + SX1509_EDGE_SENSE_REG_LOW_HIGH, + LOBYTE(reg->edge_sense), + HIBYTE(reg->edge_sense)) != RETURN_OK) { + goto cleanup; + } + break; + default: + LOGGER_ERROR("SX1509: Unknown bank number: %d\n", bank); + goto cleanup; + } + + pu_en = ((pin->hw_cfg & OCGPIO_CFG_IN_PULL_MASK) == + OCGPIO_CFG_IN_PU); + pd_en = ((pin->hw_cfg & OCGPIO_CFG_IN_PULL_MASK) == + OCGPIO_CFG_IN_PD); + } + + /* Set pull-up/down registers */ + reg->pull_up = set_bit8(reg->pull_up, pin_idx, pu_en); + if (ioexp_led_config_pullup(&sx_cfg->i2c_dev, + bank, + reg->pull_up, + 0x00) != RETURN_OK) { + goto cleanup; + } + + reg->pull_down = set_bit8(reg->pull_down, pin_idx, pd_en); + if (ioexp_led_config_pulldown(&sx_cfg->i2c_dev, + bank, + reg->pull_down, + 0x00) != RETURN_OK) { + goto cleanup; + } + + /* Set pin direction (0 output, 1 input) */ + reg->direction = set_bit8(reg->direction, pin_idx, io_cfg.dir); + if (ioexp_led_config_data_direction(&sx_cfg->i2c_dev, + bank, + reg->direction, + 0x00) != RETURN_OK) { + goto cleanup; + } + + /* The SX1509 doesn't support drive strength */ + res = OCGPIO_SUCCESS; + } +cleanup: + GateMutex_leave(obj->mutex, mutexKey); + return res; +} + +/***************************************************************************** + ** FUNCTION NAME : GpioSX1509_setCallback + ** + ** DESCRIPTION : set call back function for handling interrupts. + ** + ** ARGUMENTS : pointer to pin , call back function pointer , context + ** to be passed as argument to the call back function + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioSX1509_setCallback(const OcGpio_Pin *pin, + OcGpio_CallbackFn callback, + void *context) { + SX1509_Obj *obj = pin->port->object_data; + + /* TODO: we may want to support callback removal at some point */ + if (!callback) { + return OCGPIO_FAILURE; + } + + SX1509CallbackData *cb_entry = malloc(sizeof(SX1509CallbackData)); + if (!cb_entry) { + LOGGER_ERROR("Unable to malloc GPIO callback"); + return OCGPIO_FAILURE; + } + + *cb_entry = (SX1509CallbackData){ + .pin = pin, + .callback = callback, + .context = context, + }; + + /* find next blank entry & assign new callback data (protect against + * multiple tasks accessing simultaneously) + * Note: we don't need to worry about the actual callback function using + * a mutex, since the 'next' pointer assignment is atomic */ + const IArg mutexKey = GateMutex_enter(obj->mutex); + { + SX1509CallbackData **next = &obj->cb_data[pin->idx]; + while (*next) { + next = &(*next)->next; + } + *next = cb_entry; + } + GateMutex_leave(obj->mutex, mutexKey); + return OCGPIO_SUCCESS; +} + +/***************************************************************************** + ** FUNCTION NAME : GpioSX1509_disableInt + ** + ** DESCRIPTION : disable interrupts for the provided pin. + ** + ** ARGUMENTS : pointer to the pin + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioSX1509_disableInt(const OcGpio_Pin *pin) { + const SX1509_Cfg *sx_cfg = pin->port->cfg; + SX1509_Obj *obj = pin->port->object_data; + int res = OCGPIO_FAILURE; + + const sx1509RegType bank = GetBank(pin->idx); + const uint8_t pin_idx = RelativePinIdx(pin->idx); + SX1509_Registers *reg = &obj->regs[bank]; + + /* Disable (1) interrupt */ + const IArg mutexKey = GateMutex_enter(obj->mutex); + { + const uint8_t new_reg_value = set_bit8(reg->int_mask, pin_idx, 1); + if (ioexp_led_config_interrupt(&sx_cfg->i2c_dev, + bank, + new_reg_value, + 0x00) != RETURN_OK) { + goto cleanup; + } + reg->int_mask = new_reg_value; + res = OCGPIO_SUCCESS; + } +cleanup: + GateMutex_leave(obj->mutex, mutexKey); + return res; +} + +/***************************************************************************** + ** FUNCTION NAME : GpioSX1509_enableInt + ** + ** DESCRIPTION : enable interrupt on a gpio pin + ** + ** ARGUMENTS : pointer to the pin + ** + ** RETURN TYPE : OCGPIO_FAILURE or OCGPIO_SUCCESS + ** + *****************************************************************************/ +static int GpioSX1509_enableInt(const OcGpio_Pin *pin) { + const SX1509_Cfg *sx_cfg = pin->port->cfg; + SX1509_Obj *obj = pin->port->object_data; + int res = OCGPIO_FAILURE; + + const sx1509RegType bank = GetBank(pin->idx); + const uint8_t pin_idx = RelativePinIdx(pin->idx); + SX1509_Registers *reg = &obj->regs[bank]; + + /* Enable (0) interrupt */ + const IArg mutexKey = GateMutex_enter(obj->mutex); + { + const uint8_t new_reg_value = set_bit8(reg->int_mask, pin_idx, 0); + if (ioexp_led_config_interrupt(&sx_cfg->i2c_dev, + bank, + new_reg_value, + 0x00) != RETURN_OK) { + goto cleanup; + } + reg->int_mask = new_reg_value; + res = OCGPIO_SUCCESS; + } +cleanup: + GateMutex_leave(obj->mutex, mutexKey); + return res; +} + +const OcGpio_FnTable GpioSX1509_fnTable = { + .probe = GpioSX1509_probe, + .init = GpioSX1509_init, + .write = GpioSX1509_write, + .read = GpioSX1509_read, + .configure = GpioSX1509_configure, + .setCallback = GpioSX1509_setCallback, + .disableInt = GpioSX1509_disableInt, + .enableInt = GpioSX1509_enableInt, +}; diff --git a/firmware/src/drivers/GpioSX1509.h b/firmware/src/drivers/GpioSX1509.h new file mode 100644 index 0000000000..054a476f6e --- /dev/null +++ b/firmware/src/drivers/GpioSX1509.h @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef _GPIOSX1509_H_ +#define _GPIOSX1509_H_ + +#include "common/inc/global/ocmp_frame.h" +#include "inc/devices/sx1509.h" +#include "OcGpio.h" + +#include + +#include + +#define SX1509_NUM_BANKS 2 +#define SX1509_PINS_PER_BANK 8 +#define SX1509_PIN_COUNT (SX1509_NUM_BANKS * SX1509_PINS_PER_BANK) + +extern const OcGpio_FnTable GpioSX1509_fnTable; + +typedef struct SX1509_Cfg { + I2C_Dev i2c_dev; + OcGpio_Pin *pin_irq; +} SX1509_Cfg; + +/* Private SX1509 driver data */ +typedef struct SX1509_Registers { + uint8_t data; + uint8_t input_buf_disable; + uint8_t pull_up; + uint8_t pull_down; + uint8_t open_drain; + uint8_t polarity; + uint8_t direction; + uint8_t int_mask; + uint16_t edge_sense; /*!< Could be split into high and low if needed */ +} SX1509_Registers; + +/* TODO: possibly dedupe with GpioNative */ +typedef struct SX1509CallbackData { + const OcGpio_Pin *pin; + OcGpio_CallbackFn callback; + void *context; + struct SX1509CallbackData *next; /*!< Pointer to next pin subscriber */ +} SX1509CallbackData; + +typedef struct SX1509_Obj { + GateMutex_Handle mutex; /*!< Prevent simultaneous editing of registers */ + SX1509_Registers regs[SX1509_NUM_BANKS]; + + SX1509CallbackData *cb_data[SX1509_PIN_COUNT]; +} SX1509_Obj; + +#endif /* _GPIOSX1509_H_ */ diff --git a/firmware/src/drivers/OcGpio.c b/firmware/src/drivers/OcGpio.c new file mode 100644 index 0000000000..579f53cd9b --- /dev/null +++ b/firmware/src/drivers/OcGpio.c @@ -0,0 +1,10 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "drivers/OcGpio.h" + diff --git a/firmware/src/drivers/OcGpio.h b/firmware/src/drivers/OcGpio.h new file mode 100644 index 0000000000..1f881ff7a2 --- /dev/null +++ b/firmware/src/drivers/OcGpio.h @@ -0,0 +1,265 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#ifndef _OCGPIO_H_ +#define _OCGPIO_H_ + +#include +#include + +/* OC-GPIO functions will return a negative on failure */ +#define OCGPIO_SUCCESS 0 +#define OCGPIO_FAILURE -1 + +typedef struct OcGpio_Pin OcGpio_Pin; +typedef struct OcGpio_Port OcGpio_Port; + +typedef void (*OcGpio_CallbackFn) (const OcGpio_Pin *pin, void *context); + +/* Interface virtual function definitions */ +typedef int (*OcGpio_initFn) (const OcGpio_Port *port); +typedef int (*OcGpio_writeFn) (const OcGpio_Pin *pin, bool value); +typedef int (*OcGpio_readFn) (const OcGpio_Pin *pin); +typedef int (*OcGpio_configFn) (const OcGpio_Pin *pin, uint32_t cfg); +typedef int (*OcGpio_setCallbackFn) (const OcGpio_Pin *pin, + OcGpio_CallbackFn callback, + void *context); +typedef int (*OcGpio_disableIntFn) (const OcGpio_Pin *pin); +typedef int (*OcGpio_enableIntFn) (const OcGpio_Pin *pin); + +typedef struct OcGpio_FnTable { + OcGpio_initFn probe; + OcGpio_initFn init; /*!< Port initialization - called once */ + OcGpio_writeFn write; + OcGpio_readFn read; + OcGpio_configFn configure; + OcGpio_setCallbackFn setCallback; + OcGpio_disableIntFn disableInt; + OcGpio_enableIntFn enableInt; +} OcGpio_FnTable; + +/*! A port defines a specific driver instance to route through */ +struct OcGpio_Port { + const OcGpio_FnTable *fn_table; /*!< virtual table for driver */ + const void *cfg; /*!< driver-specific config settings */ + void *object_data; /*!< driver-specific data (in RAM) */ +}; + +/*! A pin provides us with everything we need to route data to the appropriate + * driver - a port instance, a pin index, and board-sprcific configuration + * settings + */ +struct OcGpio_Pin { + const OcGpio_Port *port; /*!< Pointer to IO driver instance */ + uint16_t idx; /*!< Driver-specific index */ + uint16_t hw_cfg; /*!< Any special attributes for the pin (eg. invert) */ +}; + +/* + * OC GPIO hardware configuration settings - these are to be used in the board + * config file since they're largely depicted by the layout. One may provide + * separate input and output configurations for bi-directional pins + * + * Note: these definitions are compatible with the TI GPIO driver configs with + * a small amount of shifting + * ============================================================================ + */ +typedef union OcGpio_HwCfg { + struct { + bool invert:1; + uint16_t in_cfg:3; + uint16_t out_cfg:3; + uint16_t out_str:2; + }; + uint16_t uint16; +} OcGpio_HwCfg; + +#define OCGPIO_CFG_POL_MASK (((uint32_t) 1) << OCGPIO_CFG_POL_LSB) +#define OCGPIO_CFG_IN_PULL_MASK (((uint32_t) 6) << OCGPIO_CFG_IN_LSB) +#define OCGPIO_CFG_OUT_OD_MASK (((uint32_t) 7) << OCGPIO_CFG_OUT_LSB) +#define OCGPIO_CFG_OUT_STR_MASK (((uint32_t) 3) << OCGPIO_CFG_OUT_STR_LSB) + +#define OCGPIO_CFG_POL_LSB 0 +#define OCGPIO_CFG_IN_LSB 1 +#define OCGPIO_CFG_OUT_LSB 4 +#define OCGPIO_CFG_OUT_STR_LSB 7 + +#define OCGPIO_CFG_POL_STD (((uint32_t) 0) << OCGPIO_CFG_POL_LSB) /*!< Standard polarity */ +#define OCGPIO_CFG_INVERT (((uint32_t) 1) << OCGPIO_CFG_POL_LSB) /*!< Polarity inverted */ + +#define OCGPIO_CFG_IN_NOPULL (((uint32_t) 0) << OCGPIO_CFG_IN_LSB) /*!< Input pin has no PU/PD */ +#define OCGPIO_CFG_IN_PU (((uint32_t) 2) << OCGPIO_CFG_IN_LSB) /*!< Input pin has Pullup */ +#define OCGPIO_CFG_IN_PD (((uint32_t) 4) << OCGPIO_CFG_IN_LSB) /*!< Input pin has Pulldown */ + +#define OCGPIO_CFG_OUT_STD (((uint32_t) 0) << OCGPIO_CFG_OUT_LSB) /*!< Output pin is not Open Drain */ +#define OCGPIO_CFG_OUT_OD_NOPULL (((uint32_t) 2) << OCGPIO_CFG_OUT_LSB) /*!< Output pin is Open Drain */ +#define OCGPIO_CFG_OUT_OD_PU (((uint32_t) 4) << OCGPIO_CFG_OUT_LSB) /*!< Output pin is Open Drain w/ pull up */ +#define OCGPIO_CFG_OUT_OD_PD (((uint32_t) 6) << OCGPIO_CFG_OUT_LSB) /*!< Output pin is Open Drain w/ pull dn */ + +#define OCGPIO_CFG_OUT_STR_LOW (((uint32_t) 0) << OCGPIO_CFG_OUT_STR_LSB) /*!< Low drive strength */ +#define OCGPIO_CFG_OUT_STR_MED (((uint32_t) 1) << OCGPIO_CFG_OUT_STR_LSB) /*!< Medium drive strength */ +#define OCGPIO_CFG_OUT_STR_HIGH (((uint32_t) 2) << OCGPIO_CFG_OUT_STR_LSB) /*!< High drive strength */ + +/* + * OC GPIO I/O configuration settings - these are to be used by drivers that + * control GPIO pins. These are settings that are generic enough that every + * GPIO driver should support. These settings aren't layout specific, but are + * instead dictated by the higher level driver (see OcGpio_configure) + * ============================================================================ + */ +typedef union OcGpio_ioCfg { + struct { + uint32_t dir:1; + uint32_t default_val:1; + uint32_t int_cfg:3; + }; + uint32_t uint32; +} OcGpio_ioCfg; + +#define OCGPIO_CFG_DIR_BIT 0 +#define OCGPIO_CFG_OUT_BIT 1 +#define OCGPIO_CFG_INT_LSB 2 + +#define OCGPIO_CFG_OUTPUT (((uint32_t) 0) << OCGPIO_CFG_DIR_BIT) /*!< Pin is an output. */ +#define OCGPIO_CFG_INPUT (((uint32_t) 1) << OCGPIO_CFG_DIR_BIT) /*!< Pin is an input. */ + +#define OCGPIO_CFG_OUT_LOW (((uint32_t) 0) << OCGPIO_CFG_OUT_BIT) /*!< Output low by default */ +#define OCGPIO_CFG_OUT_HIGH (((uint32_t) 1) << OCGPIO_CFG_OUT_BIT) /*!< Output high by default */ + +#define OCGPIO_CFG_INT_NONE (((uint32_t) 0) << OCGPIO_CFG_INT_LSB) /*!< No Interrupt */ +#define OCGPIO_CFG_INT_FALLING (((uint32_t) 1) << OCGPIO_CFG_INT_LSB) /*!< Interrupt on falling edge */ +#define OCGPIO_CFG_INT_RISING (((uint32_t) 2) << OCGPIO_CFG_INT_LSB) /*!< Interrupt on rising edge */ +#define OCGPIO_CFG_INT_BOTH_EDGES (((uint32_t) 3) << OCGPIO_CFG_INT_LSB) /*!< Interrupt on both edges */ +#define OCGPIO_CFG_INT_LOW (((uint32_t) 4) << OCGPIO_CFG_INT_LSB) /*!< Interrupt on low level */ +#define OCGPIO_CFG_INT_HIGH (((uint32_t) 5) << OCGPIO_CFG_INT_LSB) /*!< Interrupt on high level */ + +/* Wrapper functions to dispatch call to appropriate v-table + * ================================================================== + */ + +/* Since this is simply an interface definition, define the functions in the + * header to allow them to be inlined for better efficiency. Any functions added + * to this module that are more than a simple wrapper should be added to + * OcGpio.c instead. + */ + +/*! Probe the device for POStT + * probe function + * @param pin OcGPio_Port pointer to the driver instance to find the device + * @return 0 on success, negative on failure + */ +static inline int OcGpio_probe(const OcGpio_Port *port) { + if( port && port->fn_table && port->fn_table->probe) { + return port->fn_table->probe(port); + } else { + return OCGPIO_FAILURE; + } +} + +/*! Initialize the port - tempted to remove in favor of more generic device + * init function + * @param pin OcGPio_Port pointer to the driver instance to initialize + * @return 0 on success, negative on failure + */ +static inline int OcGpio_init(const OcGpio_Port *port) { + if( port && port->fn_table && port->fn_table->init) { + return port->fn_table->init(port); + } else { + return OCGPIO_FAILURE; + } +} + +/*! Write a value to a GPIO pin + * @param pin OcGPio_Pin pointer for the pin to be controlled + * @param value Boolean value to write to the pin + * @return 0 on success, negative on failure + */ +static inline int OcGpio_write(const OcGpio_Pin *pin, bool value) { + if( pin && pin->port && pin->port->fn_table && + pin->port->fn_table->write) { + return pin->port->fn_table->write(pin, value); + } else { + return OCGPIO_FAILURE; + } +} + +/*! Read a value from a GPIO pin + * @param pin OcGPio_Pin pointer for the pin to read + * @return Boolean value of pin, or negative if failure + */ +static inline int OcGpio_read(const OcGpio_Pin *pin) { + if( pin && pin->port && pin->port->fn_table && + pin->port->fn_table->read) { + return pin->port->fn_table->read(pin); + } else { + return OCGPIO_FAILURE; + } +} + +/*! Configure a GPIO pin's parameters (both io and hw params) + * @note This must be called before using the pin + * @param pin OcGPio_Pin pointer to the pin to configure + * @param cfg Bitfield of OCGPIO_CFG io values (direction, interrupt edge) + * @return 0 on success, negative on failure + */ +static inline int OcGpio_configure(const OcGpio_Pin *pin, uint32_t cfg) { + if( pin && pin->port && pin->port->fn_table && + pin->port->fn_table->configure) { + return pin->port->fn_table->configure(pin, cfg); + } else { + return OCGPIO_FAILURE; + } +} + +/*! Add a callback subscriber to an interrupt-enabled pin + * @note Multiple callbacks can be subscribed to a single pin + * @param pin OcGPio_Pin pointer to subscribe to changes on + * @param callback Function to call when interrupt is triggered + * @param context Context pointer that is passed to callback function + * @return 0 on success, negative on failure + */ +static inline int OcGpio_setCallback (const OcGpio_Pin *pin, + OcGpio_CallbackFn callback, + void *context) { + if( pin && pin->port && pin->port->fn_table && + pin->port->fn_table->setCallback) { + return pin->port->fn_table->setCallback(pin, callback, context); + } else { + return OCGPIO_FAILURE; + } +} + +/*! Disable pin interrupt + * @param pin OcGPio_Pin pointer for the pin to disable the interrupt on + * @return 0 on success, negative on failure + */ +static inline int OcGpio_disableInt(const OcGpio_Pin *pin) { + if( pin && pin->port && pin->port->fn_table && + pin->port->fn_table->disableInt) { + return pin->port->fn_table->disableInt(pin); + } else { + return OCGPIO_FAILURE; + } +} + +/*! Enable pin interrupt + * @note This must be called after OcGpio_setCallback in order to activate the + * interrupt + * @param pin OcGPio_Pin pointer for the pin to enable the interrupt on + * @return 0 on success, negative on failure + */ +static inline int OcGpio_enableInt(const OcGpio_Pin *pin) { + if( pin && pin->port && pin->port->fn_table && + pin->port->fn_table->enableInt) { + return pin->port->fn_table->enableInt(pin); + } else { + return OCGPIO_FAILURE; + } +} + +#endif /* _OCGPIO_H_ */ diff --git a/firmware/src/helpers/array.h b/firmware/src/helpers/array.h new file mode 100644 index 0000000000..a65c7074ed --- /dev/null +++ b/firmware/src/helpers/array.h @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#pragma once + +#ifndef HELPERS_ARRAY_H_ +#define HELPERS_ARRAY_H_ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +#endif /* HELPERS_ARRAY_H_ */ diff --git a/firmware/src/helpers/attribute.h b/firmware/src/helpers/attribute.h new file mode 100644 index 0000000000..380ce47aee --- /dev/null +++ b/firmware/src/helpers/attribute.h @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#pragma once + +#ifndef HELPERS_ATTRIBUTE_H_ +#define HELPERS_ATTRIBUTE_H_ + +#define PACKED __attribute__ ((__packed__)) + +#define UNUSED(x) (void)(x) + +#endif /* HELPERS_ATTRIBUTE_H_ */ diff --git a/firmware/src/helpers/math.h b/firmware/src/helpers/math.h new file mode 100644 index 0000000000..4499d13ee4 --- /dev/null +++ b/firmware/src/helpers/math.h @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#pragma once + +#ifndef HELPERS_MATH_H_ +#define HELPERS_MATH_H_ + +#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) +#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y)) + +#define CONSTRAIN(x, min, max) MIN(MAX((x), (min)), (max)) + +#endif /* HELPERS_MATH_H_ */ diff --git a/firmware/src/helpers/memory.c b/firmware/src/helpers/memory.c new file mode 100644 index 0000000000..20260a96d8 --- /dev/null +++ b/firmware/src/helpers/memory.c @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "memory.h" + +#include "inc/common/global_header.h" + +void printMemory(const void *start, size_t size) { + for (size_t i = 0; i < size; ++i) { + DEBUG("0x%02x ", ((uint8_t *)start)[i]); + + if ((i+1) % 8 == 0) { + DEBUG("\n"); + } else if ((i+1) % 4 == 0) { + DEBUG(" "); + } + } + DEBUG("\n"); +} + +uint8_t set_bit8(uint8_t byte, uint8_t bit, bool value) { + const uint8_t mask = 1 << bit; + if (value) { + byte |= mask; + } else { + byte &= ~mask; + } + return byte; +} diff --git a/firmware/src/helpers/memory.h b/firmware/src/helpers/memory.h new file mode 100644 index 0000000000..a24c30e312 --- /dev/null +++ b/firmware/src/helpers/memory.h @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#pragma once + +#ifndef HELPERS_MEMORY_H_ +#define HELPERS_MEMORY_H_ + +#include +#include +#include + +#ifndef HIWORD + #define HIWORD(x) ((uint16_t)((x) >> 16)) +#endif + +#ifndef LOWORD + #define LOWORD(x) ((uint16_t)(x)) +#endif + +#ifndef HIBYTE + #define HIBYTE(x) ((uint8_t)((x) >> 8)) +#endif + +#ifndef LOBYTE + #define LOBYTE(x) ((uint8_t)(x)) +#endif + +#define zalloc(size) calloc((size), 1) + +void printMemory(const void *start, size_t size); + +/* Sets a bit in a uint8 to the specified value (1/0) + * @param byte The original value we're modifying + * @param bit The index of the bit we want to set/clear + * @param value The value the bit should be set to (1/0) + * @return The modified byte with the bit's new value + */ +uint8_t set_bit8(uint8_t byte, uint8_t bit, bool value); + +#endif /* HELPERS_MEMORY_H_ */ diff --git a/firmware/src/interfaces/UART/uartdma.c b/firmware/src/interfaces/UART/uartdma.c new file mode 100644 index 0000000000..eec50cccb8 --- /dev/null +++ b/firmware/src/interfaces/UART/uartdma.c @@ -0,0 +1,517 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** +#include "comm/gossiper.h" +#include "common/inc/global/ocmp_frame.h" +#include "inc/interfaces/uartdma.h" +#include "inc/common/global_header.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +char input[64]; +UART_Handle uart; + +/***************************************************************************** + * HANDLES DEFINITION + *****************************************************************************/ +/* Semaphore */ +Semaphore_Handle semUART; +Semaphore_Handle semUARTTX; + +/* Queue object */ +Queue_Struct uartRxMsg; +Queue_Struct uartTxMsg; +Queue_Handle uartRxMsgQueue; +Queue_Handle uartTxMsgQueue; + +/* Global Task Configuration Variables */ +Task_Struct ocUARTDMATask; +Char ocUARTDMATaskStack[OCUARTDMA_TASK_STACK_SIZE]; + +Task_Struct ocUARTDMATxTask; +Char ocUARTDMATxTaskStack[OCUARTDMATX_TASK_STACK_SIZE]; + +/***************************************************************************** + * The transmit and receive buffers used for the UART transfers. There is one + * transmit buffer and a pair of recieve ping-pong buffers. + ******************************************************************************/ +static uint8_t ui8TxBuf[UART_TXBUF_SIZE]; +static uint8_t ui8RxBufA[UART_RXBUF_SIZE]; +static uint8_t ui8RxBufB[UART_RXBUF_SIZE]; +static uint8_t ui8uartdmaRxBuf[UART_RXBUF_SIZE]; + +/***************************************************************************** + * The control table used by the uDMA controller. This table must be aligned + * to a 1024 byte boundary. + *****************************************************************************/ +#pragma DATA_ALIGN(pui8ControlTable, 1024) + +uint8_t pui8ControlTable[1024]; + +/***************************************************************************** + * + * The interrupt handler for uDMA errors. This interrupt will occur if the + * uDMA encounters a bus error while trying to perform a transfer. This + * handler just increments a counter if an error occurs. + *****************************************************************************/ +void uDMAErrorHandler(void) +{ + uint32_t ui32Status; + + /* Check for uDMA error bit. */ + ui32Status = uDMAErrorStatusGet(); + + /* If there is a uDMA error, then clear the error and increment the error + * counter.*/ + if (ui32Status) { + uDMAErrorStatusClear(); + LOGGER_WARNING("UARTDMACTR:WARNING::Something went bad in uDMA.\n"); + } +} + +/***************************************************************************** + * + * The interrupt handler for UART4. + * + *****************************************************************************/ +void UART4IntHandler(void) +{ + uint32_t ui32Status; + uint32_t ui32Mode; + ui32Status = UARTIntStatus(UART4_BASE, 1); + + /* Clear any pending status*/ + UARTIntClear(UART4_BASE, ui32Status); + + /*Primary Buffer*/ + ui32Mode = uDMAChannelModeGet(UDMA_CHANNEL_TMR0A | UDMA_PRI_SELECT); + if (ui32Mode == UDMA_MODE_STOP) { + uDMAChannelTransferSet(UDMA_CHANNEL_TMR0A | UDMA_PRI_SELECT, + UDMA_MODE_PINGPONG, + (void *)(UART4_BASE + UART_O_DR), + ui8RxBufA, sizeof(ui8RxBufA)); + /*Preparing message to send to UART RX Queue*/ + memset(ui8uartdmaRxBuf,'\0',UART_RXBUF_SIZE); + memcpy(ui8uartdmaRxBuf,ui8RxBufA,sizeof(ui8RxBufA)); + Semaphore_post(semUART); + } + + /*Alternate Buffer*/ + ui32Mode = uDMAChannelModeGet(UDMA_CHANNEL_TMR0A | UDMA_ALT_SELECT); + if(ui32Mode == UDMA_MODE_STOP) { + uDMAChannelTransferSet(UDMA_CHANNEL_TMR0A | UDMA_ALT_SELECT, + UDMA_MODE_PINGPONG, + (void *)(UART4_BASE + UART_O_DR), + ui8RxBufB, sizeof(ui8RxBufB)); + /*Preparing message to send to UART RX Queue*/ + memset(ui8uartdmaRxBuf,'\0',UART_RXBUF_SIZE); + memcpy(ui8uartdmaRxBuf,ui8RxBufB,sizeof(ui8RxBufB)); + Semaphore_post(semUART); + } +} + +/***************************************************************************** + * Reset and configure DMA and UART. + *****************************************************************************/ +void resetUARTDMA(void) +{ + LOGGER_WARNING("UARTDMACTR:WARNING::Configuring UART DMA again.....!!!\n"); + + // Configure UART.*/ + ConfigureUART(); + + /* Configure UART.*/ + uartdma_init(); + LOGGER("UARTDMACTR:INFO::Re-Configuring UART DMA again.....!!!\n"); +} +typedef enum DK_TM4C129X_UARTName { + DK_TM4C129X_UART0 = 0, + DK_TM4C129X_UART1, + DK_TM4C129X_UART2, + DK_TM4C129X_UART3, + DK_TM4C129X_UART4, + DK_TM4C129X_UARTCOUNT +} DK_TM4C129X_UARTName; +/***************************************************************************** + * Configure the UART and its pins. This must be called before UARTprintf(). + *****************************************************************************/ +void ConfigureUART(void) +{ + LOGGER_DEBUG("UARTDMACTR:INFO::Configuring UART interface for communication.\n"); + + UART_Params uartParams; + + /* Enable the GPIO Peripheral used by the UART.*/ + SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC); + + /* Enable UART4 */ + SysCtlPeripheralEnable(SYSCTL_PERIPH_UART4); + SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UART4); + + // SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1); + // SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UART1); + + /* Configure GPIO Pins for UART mode.*/ + GPIOPinConfigure(GPIO_PC4_U4RX); + GPIOPinConfigure(GPIO_PC5_U4TX); + GPIOPinTypeUART(GPIO_PORTC_BASE, GPIO_PIN_4 | GPIO_PIN_5); + + + // GPIOPinConfigure(GPIO_PC4_U1RX); + // GPIOPinConfigure(GPIO_PC5_U1TX); + // GPIOPinTypeUART(GPIO_PORTC_BASE, GPIO_PIN_4 | GPIO_PIN_5); + + UART_init(); + + /* Create a UART with data processing off. */ + UART_Params_init(&uartParams); + uartParams.writeDataMode = UART_DATA_BINARY; + uartParams.readDataMode = UART_DATA_BINARY; + uartParams.readReturnMode = UART_RETURN_FULL; + uartParams.readEcho = UART_ECHO_OFF; + uartParams.baudRate = 115200; + + uart = UART_open(DK_TM4C129X_UART4, &uartParams); + // uart = UART_open(DK_TM4C129X_UART1, &uartParams); + +} + +/**************************************************************************** + * + * Initializes the UART4 peripheral and sets up the TX and RX uDMA channels. + *****************************************************************************/ +void InitUART4Transfer(void) +{ + LOGGER_DEBUG("UARTDMACTR:INFO::Configuring UART interrupt and uDMA channel for communication to GPP.\n"); + uint_fast16_t ui16Idx; + const uint32_t SysClock = 120000000; + + /* TX buffer init to 0.*/ + for (ui16Idx = 0; ui16Idx < UART_TXBUF_SIZE; ui16Idx++) { + ui8TxBuf[ui16Idx] = 0; + } + + /* Enable the UART peripheral, and configure it to operate even if the CPU + * is in sleep.*/ + SysCtlPeripheralEnable(SYSCTL_PERIPH_UART4); + SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UART4); + + /* Configure the UART communication parameters.*/ + + UARTConfigSetExpClk(UART4_BASE, SysClock, 115200, + UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | + UART_CONFIG_PAR_NONE); + + /* Set both the TX and RX trigger thresholds to 4. */ + UARTFIFOLevelSet(UART4_BASE, UART_FIFO_TX4_8, UART_FIFO_RX4_8); + + /* Enable the UART for operation, and enable the uDMA interface for both TX + * and RX channels.*/ + UARTEnable(UART4_BASE); + UARTDMAEnable(UART4_BASE, UART_DMA_RX | UART_DMA_TX); + + uDMAChannelAttributeDisable(UDMA_CHANNEL_TMR0A, UDMA_ATTR_ALTSELECT + | UDMA_ATTR_USEBURST | UDMA_ATTR_HIGH_PRIORITY + | UDMA_ATTR_REQMASK); + + uDMAChannelControlSet(UDMA_CHANNEL_TMR0A | UDMA_PRI_SELECT, + UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | + UDMA_ARB_4); + + uDMAChannelControlSet(UDMA_CHANNEL_TMR0A | UDMA_ALT_SELECT, + UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | + UDMA_ARB_4); + + uDMAChannelTransferSet(UDMA_CHANNEL_TMR0A | UDMA_PRI_SELECT, + UDMA_MODE_PINGPONG, + (void *)(UART4_BASE + UART_O_DR), + ui8RxBufA, sizeof(ui8RxBufA)); + + uDMAChannelTransferSet(UDMA_CHANNEL_TMR0A | UDMA_ALT_SELECT, + UDMA_MODE_PINGPONG, + (void *)(UART4_BASE + UART_O_DR), + ui8RxBufB, sizeof(ui8RxBufB)); + + uDMAChannelAttributeDisable(UDMA_CHANNEL_TMR0B, + UDMA_ATTR_ALTSELECT | UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK); + + uDMAChannelAttributeEnable(UDMA_CHANNEL_TMR0B, UDMA_ATTR_USEBURST); + + uDMAChannelControlSet(UDMA_CHANNEL_TMR0B | UDMA_PRI_SELECT, + UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_4); + + uDMAChannelTransferSet(UDMA_CHANNEL_TMR0B | UDMA_PRI_SELECT, + UDMA_MODE_BASIC, ui8TxBuf, + (void *)(UART4_BASE + UART_O_DR), + sizeof(ui8TxBuf)); + + uDMAChannelAssign(UDMA_CH18_UART4RX); + uDMAChannelAssign(UDMA_CH19_UART4TX); + + uDMAChannelEnable(UDMA_CHANNEL_TMR0A); + uDMAChannelEnable(UDMA_CHANNEL_TMR0B); + + /* Enable the UART DMA TX/RX interrupts.*/ + UARTIntEnable(UART4_BASE, UART_INT_DMARX ); + + /* Enable the UART peripheral interrupts.*/ + IntEnable(INT_UART4); +} + + +/***************************************************************************** + * Intialize UART uDMA for the data transfer. This will initialise both Tx and + * Rx Channel associated with UART Tx and Rx + ****************************************************************************/ +void uartdma_init(void) +{ + LOGGER_DEBUG("UARTDMACTR:INFO::Starting uDMA intilaization.\n"); + SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA); + SysCtlPeripheralSleepEnable(SYSCTL_PERIPH_UDMA); + IntEnable(INT_UDMAERR); + uDMAEnable(); + uDMAControlBaseSet(pui8ControlTable); + InitUART4Transfer(); +} + +/***************************************************************************** + * Initialize the UART with DMA interface. + ****************************************************************************/ +void uartDMAinterface_init(void) +{ + /* Configure UART */ + ConfigureUART(); + + /* Initialize UART */ +// uartdma_init(); + + /*UART RX Semaphore */ + LOGGER_DEBUG("UARTDMACTR:INFO:: uartDMA interface intialization.\n"); + semUART = Semaphore_create(0, NULL, NULL); + if (semUART == NULL) { + LOGGER_ERROR("UARTDMACTR:ERROR::UART RX Semaphore creation failed.\n"); + } + + /*UART OCMP RX Message Queue*/ + uartRxMsgQueue = Util_constructQueue(&uartRxMsg); + LOGGER_DEBUG("UARTDMACTR:INFO::Constructing message Queue 0x%x for UART RX OCMP Messages.\n", + uartRxMsgQueue); + + LOGGER_DEBUG("UARTDMACTR:INFO::Waiting for OCMP UART RX messgaes....!!!\n"); +} + +/***************************************************************************** + * uartdma_rx_taskfxn -Handles the UART received data. + ****************************************************************************/ +static void uartdma_rx_taskfxn(UArg arg0, UArg arg1) +{ + // Initialize application + uartDMAinterface_init(); + + // Application main loop + while (true) { + UART_read(uart, &input, 64); + // if (Semaphore_pend(semUART, BIOS_WAIT_FOREVER)) { + if(1) { + /* Reset Uart DMA if the SOF is not equal to 0X55 */ + // if (ui8uartdmaRxBuf[0] != OCMP_MSG_SOF) { + if (input[0] != OCMP_MSG_SOF) { + // resetUARTDMA(); + } else { + /* OCMP UART RX Messgaes */ + uint8_t * pWrite = NULL; + pWrite = (uint8_t *) malloc( + sizeof(OCMPMessageFrame) + OCMP_FRAME_MSG_LENGTH); + if (pWrite != NULL) { + memset(pWrite, '\0', UART_RXBUF_SIZE); + memcpy(pWrite, input, UART_RXBUF_SIZE); +#if 0 + uint8_t i = 0; + LOGGER_DEBUG("UARTDMACTR:INFO:: UART RX BUFFER:\n"); + for( i = 0; i < UART_RXBUF_SIZE; i++) + { + LOGGER_DEBUG("0x%x ",input[i]); + } + LOGGER_DEBUG("\n"); +#endif + Util_enqueueMsg(gossiperRxMsgQueue, semGossiperMsg, pWrite); + } else { + LOGGER_ERROR("UARTDMACTR:ERROR:: No memory left for Msg Length %d.\n", + UART_RXBUF_SIZE); + } + } + } + } +} + +/***************************************************************************** + * uartdma_tx_taskinit - Creating IPC objects + *****************************************************************************/ +void uartdma_tx_taskinit(void) +{ + LOGGER_DEBUG("UARTDMACTR:INFO:: uartDMA interface intialization.\n"); + + /*UART TX Semaphore */ + semUARTTX = Semaphore_create(0, NULL, NULL); + if (semUARTTX == NULL) { + LOGGER_ERROR("UARTDMACTR:ERROR::UART TX Semaphore creation failed.\n"); + } + + /*UART OCMP TX Message Queue*/ + uartTxMsgQueue = Util_constructQueue(&uartTxMsg); + LOGGER_DEBUG("UARTDMACTR:INFO::Constructing message Queue 0x%x for UART TX OCMP Messages.\n", + uartTxMsgQueue); +} + + +/***************************************************************************** + ** FUNCTION NAME : uartdma_process_tx_message + ** + ** DESCRIPTION : transmitt TX Messages to UART physical medium + ** + ** ARGUMENTS : Pointer to UARTDMATX_Evt_t structure + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static ReturnStatus uartdma_process_tx_message(uint8_t *pMsg) +{ + ReturnStatus status = RETURN_OK; + if (1) { + memset(ui8TxBuf, '\0', UART_TXBUF_SIZE); + + memcpy(ui8TxBuf, pMsg, UART_TXBUF_SIZE); +#if 0 + uint8_t i = 0; + LOGGER_DEBUG("UARTDMACTR:INFO:: UART TX BUFFER:\n"); + for( i = 0; i < UART_TXBUF_SIZE; i++) + { + LOGGER_DEBUG("0x%x ",ui8TxBuf[i]); + } + LOGGER_DEBUG("\n"); +#endif + UART_write(uart, ui8TxBuf, UART_TXBUF_SIZE); + + } else { + status = RETURN_NOTOK; + } + return status; +} +/* +static ReturnStatus uartdma_process_tx_message(uint8_t *pMsg) +{ + ReturnStatus status = RETURN_OK; + if (!uDMAChannelIsEnabled(UDMA_CHANNEL_TMR0B)) { + memset(ui8TxBuf, '\0', UART_TXBUF_SIZE); + + memcpy(ui8TxBuf, pMsg, UART_TXBUF_SIZE); +#if 1 + uint8_t i = 0; + LOGGER_DEBUG("UARTDMACTR:INFO:: UART TX BUFFER:\n"); + for( i = 0; i < UART_TXBUF_SIZE; i++) + { + LOGGER_DEBUG("0x%x ",ui8TxBuf[i]); + } + LOGGER_DEBUG("\n"); +#endif + uDMAChannelTransferSet(UDMA_CHANNEL_TMR0B | UDMA_PRI_SELECT, + UDMA_MODE_BASIC, ui8TxBuf, + (void *) (UART4_BASE + UART_O_DR), sizeof(ui8TxBuf)); + uDMAChannelEnable(UDMA_CHANNEL_TMR0B); + } else { + status = RETURN_NOTOK; + } + return status; +} +*/ +/***************************************************************************** + * uartdma_tx_taskfxn - Handles the UART sent data. + ****************************************************************************/ +static void uartdma_tx_taskfxn(UArg arg0, UArg arg1) +{ + /* UARTDMA TX init*/ + uartdma_tx_taskinit(); + + // Application main loop + while (true) { + if (Semaphore_pend(semUARTTX, BIOS_WAIT_FOREVER)) { + /* OCMP UART TX Messgaes */ + while (!Queue_empty(uartTxMsgQueue)) { + uint8_t *pWrite = (uint8_t *) Util_dequeueMsg(uartTxMsgQueue); + if (pWrite) { + uartdma_process_tx_message(pWrite); + } + free(pWrite); + } + } + } +} + +/****************************************************************************** + ** FUNCTION NAME : uartdma_rx_createtask + ** + ** DESCRIPTION : Task creation function for the UARTDMA + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + ******************************************************************************/ +void uartdma_rx_createtask(void) +{ + Task_Params taskParams; + Task_Params_init(&taskParams); + taskParams.stackSize = OCUARTDMA_TASK_STACK_SIZE; + taskParams.stack = &ocUARTDMATaskStack; + taskParams.instance->name = "UART_DMA_TASK"; + taskParams.priority = OCUARTDMA_TASK_PRIORITY; + Task_construct(&ocUARTDMATask, (Task_FuncPtr) uartdma_rx_taskfxn, + &taskParams, NULL); + LOGGER_DEBUG("UARTDMACTRl:INFO::Creating UART DMA task function.\n"); +} + +/****************************************************************************** + ** FUNCTION NAME : uartdma_tx_createtask + ** + ** DESCRIPTION : Task creation function for the UARTDMA TX + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + ******************************************************************************/ +void uartdma_tx_createtask(void) +{ + Task_Params taskParams; + Task_Params_init(&taskParams); + taskParams.stackSize = OCUARTDMATX_TASK_STACK_SIZE; + taskParams.stack = &ocUARTDMATxTaskStack; + taskParams.instance->name = "UART_DMA_TX_TASK"; + taskParams.priority = OCUARTDMATX_TASK_PRIORITY; + Task_construct(&ocUARTDMATxTask, (Task_FuncPtr) uartdma_tx_taskfxn, + &taskParams, NULL); + LOGGER_DEBUG("UARTDMACTRl:INFO::Creating UART DMA TX task function.\n"); +} diff --git a/firmware/src/main.c b/firmware/src/main.c new file mode 100644 index 0000000000..dcee2f0e8e --- /dev/null +++ b/firmware/src/main.c @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +//***************************************************************************** +// HEADER FILES +//***************************************************************************** +#include "Board.h" +#include "inc/common/bigbrother.h" +#include "inc/common/global_header.h" + +#include +#include + +#define xstr(a) str(a) +#define str(a) #a + +//***************************************************************************** +// FUNCTION DECLARATIONS +//***************************************************************************** +extern int ethernet_start(void); + +static void openCellular_init(void) +{ + LOGGER_DEBUG("|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n"); + LOGGER_DEBUG("|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n"); + LOGGER_DEBUG("|||| |||| |||| ||||| |||||| |||| |||| |||| ||||||||| ||||||||| |||| |||| ||||||||| |||| ||||\n"); + LOGGER_DEBUG("|||| |||| |||| |||| |||| |||||||||| | ||||| |||| ||||||||| ||||||||| ||||||||| ||||||||| |||| |||| ||||||||| |||| |||| |||| ||||\n"); + LOGGER_DEBUG("|||| |||| |||| |||| |||| |||||||||| || |||| |||| ||||||||| ||||||||| ||||||||| ||||||||| |||| |||| ||||||||| |||| |||| |||| ||||\n"); + LOGGER_DEBUG("|||| |||| |||| |||| ||||| ||| ||| |||| ||||||||| |||| ||||||||| ||||||||| |||| |||| ||||||||| |||| ||||\n"); + LOGGER_DEBUG("|||| |||| |||| |||||||||| |||||||||| |||| || |||| ||||||||| ||||||||| ||||||||| ||||||||| |||| |||| ||||||||| |||| |||| || ||||||\n"); + LOGGER_DEBUG("|||| |||| |||| |||||||||| |||||||||| ||||| | |||| ||||||||| ||||||||| ||||||||| ||||||||| |||| |||| ||||||||| |||| |||| ||| |||||\n"); + LOGGER_DEBUG("|||| |||| |||||||||| ||||| |||||| |||| |||| |||| |||| |||| |||| |||| |||| |||| |||| ||||\n"); + LOGGER_DEBUG("|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n"); + LOGGER_DEBUG("|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n"); + LOGGER_DEBUG("\nOCWare v" + xstr(_FW_REV_MAJOR_)"." + xstr(_FW_REV_MINOR_)"." + xstr(_FW_REV_BUGFIX_)"-" + xstr(_FW_REV_TAG_)"\n"); + LOGGER_DEBUG("Build Date: "__DATE__" "__TIME__"\n\n"); +} + +static void exit_handler(int unused) +{ + /* Perform a full system reset if we fault, + * hope it doesn't happen again */ + SysCtlReset(); +} + +/*Main Function */ +int main(void) +{ + /* Install an exit handler to catch if we fault out */ +// OcGpio_Pin pin_watchdog = { &ec_io, OC_EC_WD_INPUT }; + + System_atexit(exit_handler); + openCellular_init(); + /* Call board init functions */ + Board_initGeneral(); + Board_initGPIO(); + Board_initI2C(); + +//TODO: PWR2 + // Board_initUART(); + bigbrother_createtask(); + /* Start BIOS */ + BIOS_start(); + return (0); +} diff --git a/firmware/src/post/post.c b/firmware/src/post/post.c new file mode 100644 index 0000000000..b697366aa7 --- /dev/null +++ b/firmware/src/post/post.c @@ -0,0 +1,418 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +//***************************************************************************** +// HEADER FILES +//***************************************************************************** + +#include "comm/gossiper.h" +#include "common/inc/global/ocmp_frame.h" +#include "common/inc/global/post_frame.h" +#include "inc/common/bigbrother.h" +#include "inc/common/post.h" +#include "inc/subsystem/hci/hci.h" +#include "inc/subsystem/power/power.h" +#include "inc/utils/ocmp_util.h" +#include "registry/SSRegistry.h" + +#include + +#include +#include + +//***************************************************************************** +// HANDLES DEFINITION +//***************************************************************************** + +/* Queue object */ +/* + * Semaphore for the POST task where it will be waiting on + * sub systems or BigBrother postmessages. + */ +Semaphore_Handle semPOSTMsg; + +static Queue_Struct postRxMsg; + +/* + * postRxMsgQueue - Used by the BigBrother/Subsystem to pass the POST request + * and ack message. + */ +Queue_Handle postRxMsgQueue; + +/* Global Task Configuration Variables */ +static Task_Struct postTask; +static Char postTaskStack[POST_TASK_STACK_SIZE]; + +/* POST state */ +static uint8_t postState = 0; +static OCMPSubsystem POST_subSystem; +extern POSTData PostResult[POST_RECORDS]; + +/***************************************************************************** + * FUNCTION DECLARATIONS + *****************************************************************************/ +static void post_taskfxn(UArg a0, UArg a1); +static void post_task_init(void); +static ReturnStatus post_process_msg(OCMPSubsystem OC_subSystem); +static OCMPMessageFrame* post_create_execute_msg(OCMPSubsystem OC_subSystem); +static void post_activate(OCMPMessageFrame *pPOSTMsg); +static void post_process_rx_msg(OCMPMessageFrame *pPOSTMsg); +static void post_move_to_next_subsystem(); +static void post_update_result_to_bigbrother(OCMPMessageFrame *pPOSTMsg); +extern ReturnStatus bb_sys_post_complete(void); + +/***************************************************************************** + ** FUNCTION NAME : _post_complete + ** + ** DESCRIPTION : Get POST results from EEPROM. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void _post_complete() +{ + uint8_t iter = 0; + LOGGER_DEBUG("POST:INFO::POST test is completed.\n"); + LOGGER_DEBUG("||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n"); + LOGGER_DEBUG("|||||||||||||||||||||||||||||||||||||||||||||||||||||||POST TABLE|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n"); + /* POST results */ + for (iter = 0; iter < POST_RECORDS; iter++) { + LOGGER_DEBUG("\t POST:INFO:: POSTRESULT SS: 0x%x Device S.No: 0x%x I2C Bus: 0x%x Device Addr: 0x%x Device Id: 0x%x Manufacture Id: 0x%x Status: 0x%x.\n", + PostResult[iter].subsystem, PostResult[iter].devSno, + PostResult[iter].i2cBus, PostResult[iter].devAddr, + PostResult[iter].devId, PostResult[iter].manId, + PostResult[iter].status); + } + LOGGER_DEBUG("||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n"); + LOGGER_DEBUG("||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||\n"); +} + + +/***************************************************************************** + ** FUNCTION NAME : post_data_init + ** + ** DESCRIPTION : Initializes post struct. + ** + ** ARGUMENTS : subsystem , device serial number + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void post_init_POSTData(POSTData *pData,OCMPSubsystem subsystem, uint8_t devSno) +{ + pData->subsystem = subsystem; + pData->devSno = devSno; + pData->i2cBus = 0xFF; + pData->devAddr = 0xFF; + pData->manId = 0xFFFF; + pData->devId = 0xFFFF; + pData->status = POST_DEV_MISSING; +} + +/***************************************************************************** + ** FUNCTION NAME : post_update_deviceInfo + ** + ** DESCRIPTION : Update bus, device address, manufacturing ID and device ID in post struct.\ + ** if no I2C bus is associated with device than it will be updated to 0xFF. + ** + ** ARGUMENTS : I2C Bus, Address, man Id, device Id. + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void post_update_POSTData(POSTData *pData, uint8_t I2CBus, uint8_t devAddress, uint16_t manId, uint16_t devId) +{ + pData->i2cBus = I2CBus; + pData->devAddr = devAddress; + pData->manId = manId; + pData->devId = devId; +} + +/***************************************************************************** + ** FUNCTION NAME : post_update_deviceStatus + ** + ** DESCRIPTION : Update post status + ** + ** ARGUMENTS : POSTData and status. + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void post_update_POSTStatus(POSTData *pData, ePostCode status) +{ + pData->status = status; +} + +/***************************************************************************** + ** FUNCTION NAME : post_move_to_next_subsystem + ** + ** DESCRIPTION : Move to next subssytem in the OCSubSystem. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void post_move_to_next_subsystem() +{ + POST_subSystem = (OCMPSubsystem) (POST_subSystem + (OCMPSubsystem) 1); + if (POST_subSystem > OC_SS_MAX_LIMIT) { + POST_subSystem = OC_SS_PWR; + } +} + +/***************************************************************************** + ** FUNCTION NAME : post_update_result_to_bigbrother + ** + ** DESCRIPTION : Send POST completion status to Bigbrother. + ** + ** ARGUMENTS : OCMPMessageFrame pointer for the update status + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void post_update_result_to_bigbrother(OCMPMessageFrame *pPOSTMsg) +{ + pPOSTMsg->message.subsystem = OC_SS_PWR; //OC_SUBSYSTEM_MAX_LIMIT subsystem number taken for bigbrother + memcpy((pPOSTMsg->message.ocmp_data), &postState, 1); + Util_enqueueMsg(bigBrotherRxMsgQueue, semBigBrotherMsg, + (uint8_t*) pPOSTMsg); +} + +/***************************************************************************** + ** FUNCTION NAME : post_process_msg + ** + ** DESCRIPTION : Forward excecute POST message to subsystem. + ** + ** ARGUMENTS : Subsystem + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static ReturnStatus post_process_msg(OCMPSubsystem OC_subSystem) +{ + ReturnStatus status = RETURN_OK; + if (OC_subSystem == OC_SS_MAX_LIMIT) { + _post_complete(); + POST_subSystem = OC_SS_PWR; + } else { + OCMPMessageFrame *postFrame = post_create_execute_msg(OC_subSystem); + if (postFrame) { + if (!SSRegistry_sendMessage(OC_subSystem, postFrame)) { + LOGGER_DEBUG("POST:ERROR::Subsystem %d does not exist\n", + OC_subSystem); + } + } else { + LOGGER_DEBUG("POST:ERROR::Out of memory.\n"); + status = RETURN_NOTOK; + } + } + return status; +} + +/***************************************************************************** + ** FUNCTION NAME : post_create_execute_msg + ** + ** DESCRIPTION : create execute POST message for subsystem. + ** + ** ARGUMENTS : Subsystem + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static OCMPMessageFrame* post_create_execute_msg(OCMPSubsystem OC_subSystem) +{ + LOGGER_DEBUG("POST:INFO::Activation POST for SS %d.",OC_subSystem); + OCMPMessageFrame *postExeMsg = create_ocmp_msg_frame(OC_subSystem, + OCMP_MSG_TYPE_POST, + OCMP_AXN_TYPE_ACTIVE, + 0x00,0x00,1); + return postExeMsg; +} + +/***************************************************************************** + ** FUNCTION NAME : post_create_enable_msg + ** + ** DESCRIPTION : create execute POST message for subsystem. + ** + ** ARGUMENTS : Subsystem + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static OCMPMessageFrame* post_create_enable_msg(OCMPSubsystem OC_subSystem) +{ + uint8_t dummyByte = 0xff; + OCMPActionType actionType = OCMP_AXN_TYPE_ENABLE; + LOGGER_DEBUG("POST:INFO::Enabling system for POST."); + if(OC_subSystem == OC_SS_MAX_LIMIT) { + OC_subSystem = OC_SS_PWR; + actionType = OCMP_AXN_TYPE_REPLY; + } else { + actionType = OCMP_AXN_TYPE_ENABLE; + } + OCMPMessageFrame *postExeMsg = create_ocmp_msg_frame(OC_subSystem, + OCMP_MSG_TYPE_POST, + actionType,0x00,0x00,1); + return postExeMsg; +} + + +/***************************************************************************** + ** FUNCTION NAME : post_activate + ** + ** DESCRIPTION : Processes the POST Acks received from the subssystems. + ** + ** ARGUMENTS : Pointer to POST_AckEvt_t structure + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void post_activate(OCMPMessageFrame *pPOSTMsg) +{ + ReturnStatus POSTAckstatus = RETURN_NOTOK; + LOGGER_DEBUG("POST:INFO:: Processing POST Ack received from the " + "Subsystem %d.\n",POST_subSystem); + System_flush(); + //Do the casting for the pMsg + //POSTAckstatus = (ReturnStatus) (pPOSTMsg->message.ocmp_data); + memcpy(&POSTAckstatus, pPOSTMsg->message.ocmp_data, 1); + if ( (pPOSTMsg->message.subsystem == OC_SS_PWR) + && (pPOSTMsg->message.action == OCMP_AXN_TYPE_ACTIVE) ){ + post_process_msg(POST_subSystem); + } else { + if (pPOSTMsg->message.subsystem == POST_subSystem) { + postState = (!POSTAckstatus) & postState; + LOGGER_ERROR("POST:INFO:: POST status for 0x%x Subsystem is 0x%x" + " and OC POST status is 0x%x.\n", + POST_subSystem, POSTAckstatus, postState); + if (pPOSTMsg) { + free(pPOSTMsg); + } + } + post_move_to_next_subsystem(); + post_process_msg(POST_subSystem); + } +} + +/***************************************************************************** + ** FUNCTION NAME : post_process_rx_msg + ** + ** DESCRIPTION : Processes the POST Acks received from the subssystems. + ** + ** ARGUMENTS : Pointer to POST_AckEvt_t structure + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void post_process_rx_msg(OCMPMessageFrame *pPOSTMsg) +{ + LOGGER_DEBUG("POST:INFO::Processing POST messages.\n"); + switch (pPOSTMsg->message.action) { + case OCMP_AXN_TYPE_ACTIVE: + case OCMP_AXN_TYPE_REPLY: + post_activate(pPOSTMsg); + bb_sys_post_complete(); + break; + default: + { + LOGGER_ERROR("POST::ERROR::Unkown action type 0x%x for POST" + " message.\n", pPOSTMsg->message.action); + /*TODO: Return POST fail to BB*/ + } + } +} + +/***************************************************************************** + ** FUNCTION NAME : post_task_init + ** + ** DESCRIPTION : Initializes the POST task. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void post_task_init(void) +{ + /*Creating Semaphore for RX Message Queue*/ + semPOSTMsg = Semaphore_create(0, NULL, NULL); + if (semPOSTMsg == NULL) { + LOGGER_ERROR("POST:ERROR::POST RX Semaphore creation failed.\n"); + } + /*Creating RX Message Queue*/ + postRxMsgQueue = Util_constructQueue(&postRxMsg); + LOGGER_DEBUG("POST:INFO::Constructing message Queue for 0x%x POST RX Messages.\n", + postRxMsgQueue); + + /* Reset POST state to fail */ + postState = 0; + POST_subSystem = OC_SS_PWR; + OCMPMessageFrame *postEnableMsg = create_ocmp_msg_frame(OC_SS_PWR, + OCMP_MSG_TYPE_POST, + OCMP_AXN_TYPE_ACTIVE, + 0x00, + 0x00, + 1); + /*Ask for activate permission from BB system*/ + if (postEnableMsg) { + Util_enqueueMsg(bigBrotherRxMsgQueue, semBigBrotherMsg, + (uint8_t*) postEnableMsg); + } +} + +/****************************************************************************** + ** FUNCTION NAME : post_taskfxn + ** + ** DESCRIPTION : Executes POST test for Open cellular. + ** + ** ARGUMENTS : a0, a1 - not used + ** + ** RETURN TYPE : None + ** + ******************************************************************************/ +static void post_taskfxn(UArg a0, UArg a1) +{ + post_task_init(); + while (true) { + if (Semaphore_pend(semPOSTMsg, BIOS_WAIT_FOREVER)) { + while (!Queue_empty(postRxMsgQueue)) { + OCMPMessageFrame *pWrite = (OCMPMessageFrame *) Util_dequeueMsg( + postRxMsgQueue); + if (pWrite) { + post_process_rx_msg(pWrite); + } + } + } + } +} + +/******************************************************************************* + ** FUNCTION NAME : post_createtask + ** + ** DESCRIPTION : Task creation function for the POST. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *******************************************************************************/ +void post_createtask(void) +{ + Task_Params taskParams; + + // Configure task + Task_Params_init(&taskParams); + taskParams.stack = postTaskStack; + taskParams.stackSize = POST_TASK_STACK_SIZE; + taskParams.priority = OC_POST_TASKPRIORITY; + Task_construct(&postTask, post_taskfxn, &taskParams, NULL); +} diff --git a/firmware/src/post/post_util.c b/firmware/src/post/post_util.c new file mode 100644 index 0000000000..d967f3e209 --- /dev/null +++ b/firmware/src/post/post_util.c @@ -0,0 +1,227 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "inc/common/post_util.h" +#include "inc/common/post.h" +#include "inc/common/system_states.h" +#include "platform/oc-sdr/schema/schema.h" +#include "src/registry/SSRegistry.h" + +extern const Component sys_schema[OC_SS_MAX_LIMIT]; +extern OCSubsystem *ss_reg[SUBSYSTEM_COUNT]; +POSTData PostResult[POST_RECORDS] = { { 0xFF } }; +static uint8_t deviceCount = 0; + +#ifdef UT_POST +/* + * TODO: Duplicating the definition of the following three functions from post.c for the UT framework + * If we include post.c in the UT framework , we are exposing a lot of OS dependent APIs like create_task , + * util_queue etc to the Windows Cygwin environment which will create linking issues. + * This will get fixed as part of #419 + */ + +void post_update_POSTStatus(POSTData *pData, ePostCode status) +{ + pData->status = status; +} + +void post_init_POSTData(POSTData *pData,OCMPSubsystem subsystem, uint8_t devSno) +{ + pData->subsystem = subsystem; + pData->devSno = devSno; + pData->i2cBus = 0xFF; + pData->devAddr = 0xFF; + pData->manId = 0xFFFF; + pData->devId = 0xFFFF; + pData->status = POST_DEV_MISSING; +} + +void post_update_POSTData(POSTData *pData, uint8_t I2CBus, uint8_t devAddress, uint16_t manId, uint16_t devId) +{ + pData->i2cBus = I2CBus; + pData->devAddr = devAddress; + pData->manId = manId; + pData->devId = devId; +} +#else +/* */ +/***************************************************************************** + ** FUNCTION NAME : _postDriver + ** + ** DESCRIPTION : Execute POST for a given device driver + ** (performs deep copy of alert_data) + ** + ** ARGUMENTS : pointer to subsytem, device, alert config + ** post data config and subsystem config + ** + ** RETURN TYPE : ePostCode + ** + *****************************************************************************/ +static ePostCode _postDriver(const Component *subsystem, + const Component *dev, + const AlertData *alert_data, + POSTData* postData, OCSubsystem *ss) +{ +#if 0 + if (!dev->driver) { + return POST_DEV_NO_DRIVER_EXIST; + } +#endif + ePostCode postcode = POST_DEV_FOUND; + if (dev->driver->fxnTable->cb_probe) { + postcode = dev->driver->fxnTable->cb_probe(dev->driver_cfg,postData); + post_update_POSTStatus(postData, postcode); + } + LOGGER_DEBUG("%s:INFO:: %s (%s) %s\n", subsystem->name, + dev->name, dev->driver->name, + (postcode == POST_DEV_FOUND) ? "found" : "not found"); + + if (postcode == POST_DEV_FOUND) { + if (ss->state == SS_STATE_INIT) { + if (dev->driver->fxnTable->cb_init) { + AlertData *alert_data_cp = malloc(sizeof(AlertData)); + *alert_data_cp = *alert_data; + postcode = dev->driver->fxnTable->cb_init(dev->driver_cfg, + dev->factory_config, + alert_data_cp); + } else { + postcode = POST_DEV_NO_CFG_REQ; + } + post_update_POSTStatus(postData, postcode); + LOGGER_DEBUG("%s:INFO:: Configuration for %s (%s) is %s\n", + subsystem->name, + dev->name, + dev->driver->name, + (postcode == POST_DEV_CFG_DONE) ? "ok":(postcode == POST_DEV_NO_CFG_REQ) ? "not required." : "failed."); + } + } +} + +/***************************************************************************** + ** FUNCTION NAME : OCMP_mallocFrame + ** + ** DESCRIPTION : API to allocate an OCMP frame of a given data length + ** + ** ARGUMENTS : size of the payload of frame + ** + ** RETURN TYPE : OCMPMessageFrame + ** + *****************************************************************************/ +ReturnStatus _execPost(OCMPMessageFrame *pMsg, + unsigned int subsystem_id) +{ + const Component *subsystem = &sys_schema[subsystem_id]; + OCSubsystem *ss = ss_reg[subsystem_id]; + /* TODO: this is messy and assumes we have a pointer to the subsystem - + * we'll want to change this once the framework is more mature */ + if (ss->state == SS_STATE_PWRON) { + ss->state = SS_STATE_INIT; + } + + /* Iterate over each component & device within the subsystem, calling + * its post callback */ + ReturnStatus status = RETURN_OK; + if((subsystem->ssHookSet)&& + (ss->state == SS_STATE_INIT)) { + if(subsystem->ssHookSet->preInitFxn) { + if (!(subsystem->ssHookSet->preInitFxn(subsystem->driver_cfg, &(ss->state)))) { + status = RETURN_NOTOK; + return status; + } + } + } + POSTData postData; + ePostCode postcode = POST_DEV_MISSING; + uint8_t devSno = 0; + const Component *comp = &subsystem->components[0]; + for (uint8_t comp_id = 0; (comp && comp->name); ++comp, ++comp_id) { /* Component level (ec, ap, ch1, etc.) */ + /* If we have a driver at the component level, init */ + AlertData alert_data = { + .subsystem = (OCMPSubsystem)subsystem_id, + .componentId = comp_id, + .deviceId = 0, + }; + if(!comp->components) { + if (comp->postDisabled == POST_DISABLED ) { + continue ; + } + devSno++; + post_init_POSTData(&postData,subsystem_id,devSno); + //TODO: If postcode is configuration failure what should beth recovery action. + if (_postDriver(subsystem, comp, &alert_data, &postData, ss) == POST_DEV_NO_DRIVER_EXIST) { + devSno--; + } else { + post_update_POSTresult(&postData); + } + } else { + const Component *dev = &comp->components[0]; + for (uint8_t dev_id = 0; (dev && dev->name); ++dev, ++dev_id) { /* Device level (ts, ina, etc) */ + AlertData alert_data = { + .subsystem = (OCMPSubsystem)subsystem_id, + .componentId = comp_id, + .deviceId = dev_id, + }; + if(dev->postDisabled == POST_DISABLED ) { + continue; + } + devSno++; + post_init_POSTData(&postData,subsystem_id,devSno); + if(_postDriver(subsystem, dev, &alert_data, &postData, ss) == POST_DEV_NO_DRIVER_EXIST) { + devSno--; + } else { + post_update_POSTresult(&postData); + } + } + } + } + if((subsystem->ssHookSet)&&(ss->state == SS_STATE_INIT)) { + if(subsystem->ssHookSet->postInitFxn){ + if (!(subsystem->ssHookSet->postInitFxn(subsystem->driver_cfg, &(ss->state)))) { + ss->state = SS_STATE_FAULTY; + } + } + } + if (ss->state == SS_STATE_INIT && status == RETURN_OK) { + ss->state = SS_STATE_CFG; + } else { + ss->state = SS_STATE_FAULTY; + } + + LOGGER("%s:INFO:: Modules and sensors are %s.\n", subsystem->name, + ((status == RETURN_OK) ? "initialized." : "not initialized.")); + return status; +} + + +/* ***************************************************************************** + ** FUNCTION NAME : post_update_POSTresult + ** + ** DESCRIPTION : save post result to flash + ** + ** ARGUMENTS : a0, a1 - not used + ** + ** RETURN TYPE : None + ** + ******************************************************************************/ +void post_update_POSTresult(POSTData *postData) +{ + + /* Write a device info to flash but use a dummy function for REV B boards.*/ + uint8_t iter = 0; + /* Dump structure at particular location*/ + if ( (postData->subsystem == OC_SS_PWR) && (postData->devSno == 1 ) ) { + deviceCount = 0; + memset(PostResult, '\0', (POST_RECORDS * sizeof(POSTData))); + } else { + deviceCount++; + } + //LOGGER_DEBUG("POST:INFO:: Updating POST results for the Subsystem %d , Device Serial offset %d , Total Number of records %d.\n", + // postData->subsystem,postData->devSno,deviceCount+1); + memcpy(&PostResult[deviceCount],postData,sizeof(POSTData)); +} +#endif diff --git a/firmware/src/registry/SSRegistry.c b/firmware/src/registry/SSRegistry.c new file mode 100644 index 0000000000..27c2893e7c --- /dev/null +++ b/firmware/src/registry/SSRegistry.c @@ -0,0 +1,662 @@ +/** +* Copyright (c) 2017-present, Facebook, Inc. +* All rights reserved. +* +* This source code is licensed under the BSD-style license found in the +* LICENSE file in the root directory of this source tree. An additional grant +* of patent rights can be found in the PATENTS file in the same directory. +*/ + +#include "common/inc/global/Framework.h" +#include "helpers/array.h" +#include "inc/common/bigbrother.h" /* For sending msg back via BB */ +#include "inc/common/post.h" +#include "inc/common/post_util.h" /* For sending POST response */ +#include "inc/common/global_header.h" +#include "inc/utils/ocmp_util.h" +#include "inc/utils/util.h" +#include "platform/oc-sdr/schema/schema.h" +#include "SSRegistry.h" + +#include + +#define OCMP_ACTION_TYPE_GET 1 +#define OCMP_ACTION_TYPE_SET 2 +#define OCMP_ACTION_TYPE_REPLY 3 +#define OCMP_ACTION_TYPE_ACTIVE 4 +#define OCMP_ACTION_TYPE_UPDATE 5 + +/* TODO: configurable directory (allow us to target different platforms) */ + +#define OC_TASK_STACK_SIZE 1000 +#define OC_TASK_PRIORITY 2 + +static char OC_task_stack[SUBSYSTEM_COUNT][OC_TASK_STACK_SIZE]; + +extern const Component sys_schema[SUBSYSTEM_COUNT]; + +OCSubsystem *ss_reg[SUBSYSTEM_COUNT] = {}; + +static const size_t PARAM_SIZE_MAP[] = { + [TYPE_NULL] = 0, + [TYPE_INT8] = sizeof(int8_t), + [TYPE_UINT8] = sizeof(uint8_t), + [TYPE_INT16] = sizeof(int16_t), + [TYPE_UINT16] = sizeof(uint16_t), + [TYPE_INT32] = sizeof(int32_t), + [TYPE_UINT32] = sizeof(uint32_t), + [TYPE_INT64] = sizeof(int64_t), + [TYPE_UINT64] = sizeof(uint64_t), + [TYPE_STR] = 1, /* TODO: properly handle strings */ + [TYPE_BOOL] = sizeof(bool), + [TYPE_ENUM] = 1, /* TODO: this really depends on enum - param_size should + iterate over definition to determine size requirement*/ +}; + +/***************************************************************************** + ** FUNCTION NAME : _subcompCount + ** + ** DESCRIPTION : API to calculate number of components + ** + ** ARGUMENTS : pointer to the component structure + ** + ** RETURN TYPE : int + ** + *****************************************************************************/ +static unsigned int _subcompCount(const Component *comp) { + unsigned int i = 0; + if (comp) { + while (comp->components[i].name) { + ++i; + } + } + return i; +} + +/***************************************************************************** + ** FUNCTION NAME : _paramSize + ** + ** DESCRIPTION : API to calculate size of parameter from schema + ** + ** ARGUMENTS : pointer to the parameter structure + ** + ** RETURN TYPE : size_t + ** + *****************************************************************************/ +static size_t _paramSize(const Parameter *param) { + if (!param || (param->type >= ARRAY_SIZE(PARAM_SIZE_MAP))) { + return 0; + } + + if (param->type == TYPE_STR) { + return param->size; + } + return PARAM_SIZE_MAP[param->type]; +} + +/***************************************************************************** + ** FUNCTION NAME : _compIsValid + ** + ** DESCRIPTION : API to check if component is valid + ** + ** ARGUMENTS : pointer to component + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _compIsValid(const Component *comp) +{ + return comp && comp->name; +} + +/***************************************************************************** + ** FUNCTION NAME : _paramIsValid + ** + ** DESCRIPTION : API to check if the parameter is valid + ** + ** ARGUMENTS : pointer to parameter + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _paramIsValid(const Parameter *param) +{ + return param && param->name; +} + +/***************************************************************************** + ** FUNCTION NAME : OCMP_mallocFrame + ** + ** DESCRIPTION : alert handler at the SS registry level + ** + ** ARGUMENTS : pointer to alert_data , alert id , pointer to payload + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void OCMP_GenerateAlert(const AlertData *alert_data, + unsigned int alert_id, + const void *data) +{ + if (!alert_data) { + return; + } + + const Component *subsystem = &sys_schema[alert_data->subsystem]; + const Component *component = &subsystem->components[alert_data->componentId]; + const Driver *driver = component->components[alert_data->deviceId].driver; + const Parameter *param = &driver->alerts[alert_id]; + + /* Count all previous parameters before this component */ + unsigned int param_sum = 0; + for (int i = 0; i < alert_data->deviceId; ++i) { + const Parameter *param = &component->components[i].driver->alerts[0]; + for (; _paramIsValid(param); ++param) { + param_sum += 1; + } + } + + uint16_t parameters = 0x01 << (param_sum + alert_id); + + /* Align to 4 byte boundary (bug in host) */ + size_t param_size = (_paramSize(param) + 3) & ~0x03; + + OCMPMessageFrame *pMsg = create_ocmp_msg_frame(alert_data->subsystem, + OCMP_MSG_TYPE_ALERT, + OCMP_AXN_TYPE_ACTIVE, + alert_data->componentId + 1, /* TODO: inconsistency indexing in host */ + parameters, + param_size); + if (pMsg) { + memcpy(pMsg->message.ocmp_data, data, _paramSize(param)); + Util_enqueueMsg(bigBrotherTxMsgQueue, semBigBrotherMsg, + (uint8_t*) pMsg); + } else { + LOGGER_ERROR("ERROR::Unable to allocate alert packet\n"); + } +} + +/***************************************************************************** + ** FUNCTION NAME : _handleMsgTypeCmd + ** + ** DESCRIPTION : handle all messages with command msgtype + ** + ** ARGUMENTS : pointer to message, pointer to compoenet + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _handleMsgTypeCmd(OCMPMessageFrame *pMsg, const Component *comp) +{ + const Command *cmd; + Component *dev; + if (comp) { + if (pMsg->message.parameters > 0) { + dev = &comp->components[(pMsg->message.parameters)-1]; + } else { + dev = comp; + } + if (dev->driver && dev->driver->commands) { + cmd = &dev->driver->commands[pMsg->message.action]; + } else { + cmd = &dev->commands[pMsg->message.action]; + } + if (cmd && cmd->cb_cmd) { + cmd->cb_cmd(dev->driver_cfg, pMsg->message.ocmp_data); + return true; + } + } + return false; +} + +/***************************************************************************** + ** FUNCTION NAME : OCMP_mallocFrame + ** + ** DESCRIPTION : handle message of config msgtype with set/get + ** actiontype + ** + ** ARGUMENTS : pointer to message , pointer to component, pointer to + ** parameter id , double pointer to the message payload + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _handle_cmd_get(OCMPMessageFrame *pMsg, const Component *comp, + unsigned int param_id, void *buf_ptr) +{ + switch (pMsg->message.msgtype) { + case OCMP_MSG_TYPE_CONFIG: + return (comp->driver->fxnTable->cb_get_config && + comp->driver->fxnTable->cb_get_config(comp->driver_cfg, param_id, + buf_ptr)); + case OCMP_MSG_TYPE_STATUS: + return (comp->driver->fxnTable->cb_get_status && + comp->driver->fxnTable->cb_get_status(comp->driver_cfg, param_id, + buf_ptr)); + default: + return false; + } +} + +/***************************************************************************** + ** FUNCTION NAME : _handle_cmd_set + ** + ** DESCRIPTION : handle message of config msgtype with set actiontype + ** + ** ARGUMENTS : pointer to message , pointer to component , pointer to + ** parameter id , pointer to the message payload + ** + ** RETURN TYPE : OCMPMessageFrame + ** + *****************************************************************************/ +static bool _handle_cmd_set(OCMPMessageFrame *pMsg, const Component *comp, + unsigned int param_id, const void *data) +{ + switch (pMsg->message.msgtype) { + case OCMP_MSG_TYPE_CONFIG: + return (comp->driver->fxnTable->cb_set_config && + comp->driver->fxnTable->cb_set_config(comp->driver_cfg, param_id, + data)); + default: + return false; + } +} + +/***************************************************************************** + ** FUNCTION NAME : _handleDevStatCfg + ** + ** DESCRIPTION : handle status and config msgtype messages + ** + ** ARGUMENTS : pointer to message , pointer to device, pointer to + ** parameter id , double pointer to the message payload + ** + ** RETURN TYPE : OCMPMessageFrame + ** + *****************************************************************************/ +static bool _handleDevStatCfg(OCMPMessageFrame *pMsg, const Component *dev, + unsigned int *param_id, uint8_t **buf_ptr) +{ + if (!dev->driver) { + return false; + } + + const Parameter *param_list = NULL; + switch (pMsg->message.msgtype) { + case OCMP_MSG_TYPE_CONFIG: + param_list = dev->driver->config; + break; + case OCMP_MSG_TYPE_STATUS: + param_list = dev->driver->status; + break; + default: + return false; + } + + if (!param_list) { + return false; + } + + bool dev_handled = false; + unsigned int normalized_id = 0; + while (param_list[normalized_id].name) { + if (pMsg->message.parameters & (1 << *param_id)) { + switch (pMsg->message.action) { + case OCMP_ACTION_TYPE_GET: + if (_handle_cmd_get(pMsg, dev, normalized_id, + *buf_ptr)) { + dev_handled = true; + } else { + pMsg->message.parameters &= ~(1 << *param_id); + } + break; + case OCMP_ACTION_TYPE_SET: + if (_handle_cmd_set(pMsg, dev, normalized_id, + *buf_ptr)) { + dev_handled = true; + } else { + pMsg->message.parameters &= ~(1 << *param_id); + } + break; + default: + pMsg->message.parameters &= ~(1 << *param_id); + break; + } + } + if (!dev->driver->payload_fmt_union) { + *buf_ptr += _paramSize(¶m_list[normalized_id]); + } + (*param_id)++; + normalized_id++; + } + return dev_handled; +} + +/***************************************************************************** + ** FUNCTION NAME : _handle_post_enable + ** + ** DESCRIPTION : handle post messages with enable action type + ** + ** ARGUMENTS : pointer to message, subsystem id + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _handle_post_enable(const Component *comp, OCMPMessageFrame *pMsg) +{ + bool ret = false; + OCMPMessageFrame *buffer; + const Post *postCmd = &comp->driver->post[(pMsg->message.action)-1]; + if (postCmd && postCmd->cb_postCmd) { + ret = postCmd->cb_postCmd(&buffer); + if (ret) { + Util_enqueueMsg(postRxMsgQueue, semPOSTMsg, (uint8_t*)buffer); + } + } + pMsg->message.ocmp_data[0] = !(ret); //RETURN_OK =0; + return ret; +} + +/***************************************************************************** + ** FUNCTION NAME : _handle_post_active + ** + ** DESCRIPTION : handle post message with active action type + ** + ** ARGUMENTS : pointer to message, subsystem id + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _handle_post_active(OCMPMessageFrame *pMsg,unsigned int subsystem_id) +{ + ReturnStatus status = _execPost(pMsg, subsystem_id); + return (status == RETURN_OK); +} + +/***************************************************************************** + ** FUNCTION NAME : _handle_post_get_results + ** + ** DESCRIPTION : handles messages with get result actiontype for post + ** + ** ARGUMENTS : pointer to component , pointer to message + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _handle_post_get_results(const Component *comp,OCMPMessageFrame *pMsg) +{ + bool ret = false; + const Post *postCmd = &comp->driver->post[(pMsg->message.action)-1]; + if (postCmd && postCmd->cb_postCmd) { + postCmd->cb_postCmd(pMsg); + ret = true; + } + return ret; +} + +/***************************************************************************** + ** FUNCTION NAME : _handleMsgTypePOST + ** + ** DESCRIPTION : handles all messages with post msgtype + ** + ** ARGUMENTS : pointer to message, pointer to comp, subsytem id + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _handleMsgTypePOST(OCMPMessageFrame *pMsg, const Component *comp, unsigned int subsystem_id) +{ + /* Determine driver & parameter */ + unsigned int param_id = 0; + uint8_t *buf_ptr = pMsg->message.ocmp_data; + bool dev_handled = false; + switch (pMsg->message.action) { + case OCMP_ACTION_TYPE_SET: + if (_handle_post_enable(comp, pMsg)) { + dev_handled = true; + } + break; + case OCMP_ACTION_TYPE_ACTIVE: + if (_handle_post_active(pMsg,subsystem_id)) { + dev_handled = true; + } + break; + case OCMP_ACTION_TYPE_GET: + if (_handle_post_get_results(comp, pMsg)) { + dev_handled = true; + } + break; +/* case OCMP_ACTION_REPLY: + if (_handle_post_reply(pMsg, *buf_ptr)) { + dev_handled = true; + } + break;*/ + default: + break; + } + return dev_handled; +} + +/***************************************************************************** + ** FUNCTION NAME : _handleMsgTypeStatCfg + ** + ** DESCRIPTION : handles status and config msg type + ** + ** ARGUMENTS : pointer to the msg and pointer to the comp + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool _handleMsgTypeStatCfg(OCMPMessageFrame *pMsg, const Component *comp) +{ + /* Determine driver & parameter */ + unsigned int param_id = 0; + uint8_t *buf_ptr = pMsg->message.ocmp_data; + bool dev_handled = false; + + /* Handle component-level driver */ + if (_handleDevStatCfg(pMsg, comp, ¶m_id, &buf_ptr)) { + dev_handled = true; + } + + /* Handle sub-components (devices) */ + const Component *dev = &comp->components[0]; + for (; _compIsValid(dev); ++dev) { + if (_handleDevStatCfg(pMsg, dev, ¶m_id, &buf_ptr)) { + dev_handled = true; + } + } + return dev_handled; +} + +/***************************************************************************** + ** FUNCTION NAME : ocmp_route + ** + ** DESCRIPTION : Routing function which calls the required ocmp layer + ** + ** ARGUMENTS : pointer to the message frame, subsytem id + ** + ** RETURN TYPE : true or false + ** + *****************************************************************************/ +static bool ocmp_route(OCMPMessageFrame *pMsg, unsigned int subsystem_id) +{ + const Component *subsystem = &sys_schema[subsystem_id]; + /* Validate component ID */ + if (pMsg->message.componentID > _subcompCount(subsystem)) { + LOGGER_ERROR("Component %d out of bounds\n", pMsg->message.componentID); + return false; + } + const Component *comp = &subsystem->components[(pMsg->message.componentID)-1]; + /* TODO: clean up special handling for commands */ + bool dev_handled = false; + switch (pMsg->message.msgtype) { + case OCMP_MSG_TYPE_COMMAND: + dev_handled = _handleMsgTypeCmd(pMsg, comp); + break; + case OCMP_MSG_TYPE_CONFIG: + case OCMP_MSG_TYPE_STATUS: + dev_handled = _handleMsgTypeStatCfg(pMsg, comp); + break; + case OCMP_MSG_TYPE_POST: + dev_handled = _handleMsgTypePOST(pMsg, comp, subsystem_id); + //pMsg->message.action = OCMP_ACTION_TYPE_REPLY; + //Util_enqueueMsg(postRxMsgQueue, semPOSTMsg, (uint8_t*) pMsg); + break; + default: + break; + } + + /* If we couldn't handle this message, return error */ + if (!dev_handled) { + pMsg->message.parameters = 0x00; + } + /* The main exception to the flow right now is POST - check for it first */ + if ((pMsg->message.msgtype == OCMP_MSG_TYPE_POST) && (pMsg->message.action == OCMP_ACTION_TYPE_ACTIVE)) { + pMsg->message.action = OCMP_ACTION_TYPE_REPLY; + Util_enqueueMsg(postRxMsgQueue, semPOSTMsg, (uint8_t*) pMsg); + } else if ((pMsg->message.msgtype == OCMP_MSG_TYPE_POST) && + (pMsg->message.action == OCMP_ACTION_TYPE_UPDATE)) { + Util_enqueueMsg(bigBrotherTxMsgQueue, semBigBrotherMsg, (uint8_t *)pMsg); + } else { + /* Send reply to the middleware */ + pMsg->message.action = OCMP_ACTION_TYPE_REPLY; + Util_enqueueMsg(bigBrotherTxMsgQueue, semBigBrotherMsg, (uint8_t *)pMsg); + } + return true; +} + +/***************************************************************************** + ** FUNCTION NAME : _subsystem_event_loop + ** + ** DESCRIPTION : subsytem module which is waits for message triger + ** from SSRegistry task + ** + ** ARGUMENTS : OCSubsystem, subsytem id + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void _subsystem_event_loop(UArg a0, UArg a1) +{ + OCSubsystem *ss = (OCSubsystem *)a0; + if (!ss) { + return; + } + + while (1) { + if (Semaphore_pend(ss->sem, BIOS_WAIT_FOREVER)) { + while (!Queue_empty(ss->msgQueue)) { + OCMPMessageFrame *pMsg = + (OCMPMessageFrame *) Util_dequeueMsg(ss->msgQueue); + + if (pMsg) { + /* Attempt to route the message to the correct driver + (if successful, no need to clean up message here) */ + if (!ocmp_route(pMsg, a1)) { + LOGGER_ERROR("ERROR:: Unable to route OCMP message\n"); + free(pMsg); + } + } + } + } + } +} + +/***************************************************************************** + ** FUNCTION NAME : subsystem_init + ** + ** DESCRIPTION : API to initialise ss_reg and create subsytem tasks + ** + ** ARGUMENTS : subsytem id + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +static void subsystem_init(OCMPSubsystem ss_id) { + OCSubsystem *ss = (OCSubsystem*)malloc(sizeof(OCSubsystem)); + if (!ss) { + return; + } + ss_reg[ss_id] = ss; + ss->state = SS_STATE_PWRON; + + /* Create Semaphore for RX Message Queue */ + Semaphore_construct(&ss->semStruct, 0, NULL); + ss->sem = Semaphore_handle(&ss->semStruct); + if (!ss->sem) { + LOGGER_DEBUG("SS REG:ERROR:: Failed in Creating RX Semaphore for " + "subsystem %d\n", ss_id); + } + + /* Create Message Queue for RX Messages */ + ss->msgQueue = Util_constructQueue(&ss->queueStruct); + if (!ss->msgQueue) { + LOGGER_ERROR("SS REG:ERROR:: Failed in Constructing Message Queue for " + "RX Message for subsystem %d\n", ss_id); + } + + /* Spin up the task */ + Task_Params taskParams; + Task_Params_init(&taskParams); + taskParams.stack = OC_task_stack[ss_id];// ss->taskStack; + taskParams.stackSize = OC_TASK_STACK_SIZE;//ss->taskStackSize; + taskParams.priority = OC_TASK_PRIORITY;//ss->taskPriority; + taskParams.arg0 = (UArg)ss; + taskParams.arg1 = ss_id; + + Task_construct(&ss->taskStruct, _subsystem_event_loop, &taskParams, NULL); + LOGGER_DEBUG("SS REG:DEBUG:: Creating Task for Subsystem %d\n", ss_id); +} + +/***************************************************************************** + ** FUNCTION NAME : SSRegistry_init + ** + ** DESCRIPTION : initialise the ss_reg sturcture during system init + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : None + ** + *****************************************************************************/ +void SSRegistry_init(void) { + for (OCMPSubsystem i = (OCMPSubsystem)0; i < SUBSYSTEM_COUNT; ++i) { + subsystem_init(i); + } +} + +/***************************************************************************** + ** FUNCTION NAME : SSRegistry_Get + ** + ** DESCRIPTION : API to return the ss registry entry given the + ** subsytem id + ** + ** ARGUMENTS : subsystem id + ** + ** RETURN TYPE : OCSubsystem* + ** + *****************************************************************************/ +OCSubsystem* SSRegistry_Get(OCMPSubsystem ss_id) { + if (ss_id >= SUBSYSTEM_COUNT) { + return NULL; + } + return ss_reg[ss_id]; +} + +/***************************************************************************** + ** FUNCTION NAME : SSRegistry_sendMessage + ** + ** DESCRIPTION : API to send message to the desired susbsytem + ** + ** ARGUMENTS : subsytem id , pointer to the msg to be sent + ** + ** RETURN TYPE : TRUE or FALSE + ** + *****************************************************************************/ +bool SSRegistry_sendMessage(OCMPSubsystem ss_id, void *pMsg) { + OCSubsystem *ss = SSRegistry_Get(ss_id); + if (!ss) { + return false; + } + + return Util_enqueueMsg(ss->msgQueue, ss->sem, (uint8_t*) pMsg); +} diff --git a/firmware/src/registry/SSRegistry.h b/firmware/src/registry/SSRegistry.h new file mode 100644 index 0000000000..e19d345073 --- /dev/null +++ b/firmware/src/registry/SSRegistry.h @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#ifndef _SSREGISTRY_H_ +#define _SSREGISTRY_H_ + +#include "common/inc/global/ocmp_frame.h" +#include "inc/common/system_states.h" + +#include +#include +#include + +#include + +typedef void (*SS_ProcessMsg_Cb)(OCMPMessageFrame *pBmsMsg); +/** + * Common subsystem attributes (message queue, task entry point, etc.) + */ +typedef struct OCSubsystem { + /* Message queue handles */ + Queue_Handle msgQueue; + Semaphore_Handle sem; + + /* Private variables (reduce dynamic allocation needs) */ + Queue_Struct queueStruct; + Semaphore_Struct semStruct; + Task_Struct taskStruct; + eSubSystemStates state; +} OCSubsystem; + +/** + * Initializes the subsystem registry by creating message queues + * and spawning the tasks for each subsystem + */ +void SSRegistry_init(void); + +/** + * Retrieves the pointer to the #OCSubsystem struct associated with a given ID + * @param ss_id The ID of the requested subsystem + * @return #OCSubsystem pointer if valid ID, else NULL + */ +OCSubsystem* SSRegistry_Get(OCMPSubsystem ss_id); + +/** + * Enters a message into the desired subsystem's message queue + * @note This is mostly for legacy support for the alerts manager + * @param ss_id ID of the subsystem to send the message to + * @param pMsg Message pointer to be inserted into the subsystem's queue + * @return true if successful, false if subsystem not found or queue full + */ +bool SSRegistry_sendMessage(OCMPSubsystem ss_id, void *pMsg); + +#endif /* _SSREGISTRY_H_ */ diff --git a/firmware/src/subsystem/power/power.c b/firmware/src/subsystem/power/power.c new file mode 100644 index 0000000000..07d969d9ce --- /dev/null +++ b/firmware/src/subsystem/power/power.c @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +//***************************************************************************** +// HEADER FILES +/* Board Header files */ +#include "common/inc/global/Framework.h" +#include "inc/subsystem/power/power.h" +#include "inc/common/post.h" + +#define SUBSYTEM_CHECK (PostResult[iter].subsystem <= getpostResultMsg->message.ocmp_data[0]) +extern POSTData PostResult[POST_RECORDS]; + +/***************************************************************************** + ** FUNCTION NAME : psuCore_pre_init + ** + ** DESCRIPTION : Get POST results from EEPROM. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : bool + ** + *****************************************************************************/ +bool psuCore_pre_init(void *driver, void *returnValue) +{ + //Configuring GPIOS + PWRSRC_Dev *gpioCfg = (PWRSRC_Dev *)driver; + + OcGpio_configure(&gpioCfg->cfg.pin_dc_present, OCGPIO_CFG_INPUT); + OcGpio_configure(&gpioCfg->cfg.pin_poe_prsnt_n, OCGPIO_CFG_INPUT); + OcGpio_configure(&gpioCfg->cfg.pin_int_bat_prsnt, OCGPIO_CFG_INPUT); + OcGpio_configure(&gpioCfg->cfg.pin_disable_dc_input, OCGPIO_CFG_OUTPUT); + OcGpio_configure(&gpioCfg->cfg.pin_dc_input_fault, OCGPIO_CFG_INPUT); + OcGpio_configure(&gpioCfg->cfg.pin_oc_input_present, OCGPIO_CFG_INPUT); + OcGpio_configure(&gpioCfg->cfg.pin_power_off, OCGPIO_CFG_OUTPUT); + OcGpio_write(&gpioCfg->cfg.pin_disable_dc_input, false); + OcGpio_write(&gpioCfg->cfg.pin_power_off, false); + + return true; +} + +/***************************************************************************** + ** FUNCTION NAME : PWR_post_get_results + ** + ** DESCRIPTION : Get POST results from EEPROM. + ** + ** ARGUMENTS : None + ** + ** RETURN TYPE : bool + ** + *****************************************************************************/ +bool PWR_post_get_results(void **getpostResult) +{ + ReturnStatus status = RETURN_OK; + /* Return the POST results*/ + uint8_t iter = 0x00; + uint8_t index = 0x00; + OCMPMessageFrame *getpostResultMsg = (OCMPMessageFrame *)getpostResult; + /* Get the subsystem info for which message is required */ + OCMPMessageFrame *postResultMsg = create_ocmp_msg_frame( + getpostResultMsg->message.subsystem, OCMP_MSG_TYPE_POST, + 0x05,0x00,0x00,40); + if (postResultMsg) { + /* Getting data assigned*/ + postResultMsg->header.ocmpSof = getpostResultMsg->header.ocmpSof; + postResultMsg->header.ocmpInterface = getpostResultMsg->header + .ocmpInterface; + postResultMsg->header.ocmpSeqNumber = getpostResultMsg->header + .ocmpSeqNumber; + for (iter = 0; SUBSYTEM_CHECK; iter++) { + if (PostResult[iter].subsystem + == getpostResultMsg->message.ocmp_data[0]) { + postResultMsg->message.ocmp_data[(3 * index) + 0] = + PostResult[iter].subsystem; + postResultMsg->message.ocmp_data[(3 * index) + 1] = + PostResult[iter].devSno; //Device serial Number + postResultMsg->message.ocmp_data[(3 * index) + 2] = + PostResult[iter].status; //Status ok + index++; + } + } + LOGGER_DEBUG("POWER:INFO::POST message sent for subsystem 0x%x.\n"); + /*Size of payload*/ + postResultMsg->header.ocmpFrameLen = index * 3; + /*Updating Subsystem*/ + //postResultMsg->message.subsystem = (OCMPSubsystem)PostResult[iter].subsystem; + /* Number of devices found under subsystem*/ + postResultMsg->message.parameters = index; + index = 0; + } else { + LOGGER("POWER:ERROR:: Failed to allocate memory for POST results.\n"); + } + memcpy(((OCMPMessageFrame*)getpostResult), postResultMsg, 64); + return status; +} diff --git a/firmware/src/utils/ocmp_util.c b/firmware/src/utils/ocmp_util.c new file mode 100644 index 0000000000..5edc4c66f4 --- /dev/null +++ b/firmware/src/utils/ocmp_util.c @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +/***************************************************************************** + * HEADER FILES + *****************************************************************************/ +#include "inc/utils/ocmp_util.h" + +/***************************************************************************** + ** FUNCTION NAME : OCMP_mallocFrame + ** + ** DESCRIPTION : API to allocate an OCMP frame of a given data length + ** + ** ARGUMENTS : size of the payload of frame + ** + ** RETURN TYPE : OCMPMessageFrame + ** + *****************************************************************************/ +OCMPMessageFrame * OCMP_mallocFrame(uint16_t len) +{ + OCMPMessageFrame *pMsg; + // Allocate memory for NPI Frame + pMsg = (OCMPMessageFrame *)malloc(sizeof(OCMPMessageFrame)+len); + if (pMsg != NULL) { + // Assign Data Length of Frame + pMsg->header.ocmpFrameLen = len; + // Assign pData to first byte of payload + // Pointer arithmetic of + 1 is equal to sizeof(OCMPMessageFrame) bytes + // then cast to unsigned char * for pData + //pMsg->message.ocmp_data = (unsigned char *)(pMsg + 1); + //pMsg->message.ocmp_data = (unsigned char *)(pMsg + 2); + } + return pMsg; +} + +/***************************************************************************** + ** FUNCTION NAME : create_ocmp_msg_frame + ** + ** DESCRIPTION : Create a OCMP message. + ** + ** ARGUMENTS : OCMPSubsystem subSystem, + ** OCMPMsgType msgtype, + ** OCMPActionType actionType, + ** ComponentId, + ** ParemeterID, + ** Payload size + ** + ** RETURN TYPE : OCMPMessageFrame + ** + *****************************************************************************/ +OCMPMessageFrame* create_ocmp_msg_frame(OCMPSubsystem subSystem, + OCMPMsgType msgtype, + OCMPActionType actionType, + uint8_t componentId, + uint16_t parameters, + uint8_t payloadSize) +{ + OCMPMessageFrame *ocmp_msg = (OCMPMessageFrame *) OCMP_mallocFrame( + payloadSize); + if (ocmp_msg) { + *ocmp_msg = (OCMPMessageFrame){ + .header = { + .ocmpSof = OCMP_MSG_SOF, + .ocmpInterface = OCMP_COMM_IFACE_UART, + .ocmpFrameLen = payloadSize, + //.ocmp_seqNumber = 0x00; + //.ocmp_timestamp = 0x00; //Get RTC TimeStamp + }, + .message = { + .subsystem = subSystem, + .componentID = componentId, + .parameters = parameters, + .msgtype = msgtype, + .action = actionType, + } + }; + memset(&(ocmp_msg->message.ocmp_data[0]),0x00,payloadSize); + } + return ocmp_msg; +} + +/***************************************************************************** + ** FUNCTION NAME : create_ocmp_alert_from_Evt + ** + ** DESCRIPTION : Create the OCMP Alert frame from the Event message. + ** + ** ARGUMENTS : OCMPMessageFrame to be used to create Alert, + ** ComponentId, + ** ParemeterID + ** + ** RETURN TYPE : OCMPMessageFrame + ** + *****************************************************************************/ +OCMPMessageFrame* create_ocmp_alert_from_Evt(OCMPMessageFrame* ocmpEventMsg, + uint8_t componentId, + uint16_t parameters ) +{ + OCMPMessageFrame *ocmpAlertMsg = + (OCMPMessageFrame *) OCMP_mallocFrame(1); + if (ocmpAlertMsg != NULL) { + memset(ocmpAlertMsg, 0x00, (sizeof(OCMPMessageFrame))); + memcpy(ocmpAlertMsg, ocmpEventMsg, + (sizeof(OCMPMessageFrame)) + 1); + ocmpAlertMsg->message.msgtype = OCMP_MSG_TYPE_ALERT; + ocmpAlertMsg->message.componentID = componentId; + ocmpAlertMsg->message.parameters = parameters; + } + return ocmpAlertMsg; +} diff --git a/firmware/src/utils/util.c b/firmware/src/utils/util.c new file mode 100644 index 0000000000..e94482af90 --- /dev/null +++ b/firmware/src/utils/util.c @@ -0,0 +1,329 @@ +/******************************************************************************* + Filename: util.c + Revised: $Date: 2015-06-02 11:18:40 -0700 (Tue, 02 Jun 2015) $ + Revision: $Revision: 43957 $ + + Description: This file contains utility functions. + + Copyright 2014 Texas Instruments Incorporated. All rights reserved. + + IMPORTANT: Your use of this Software is limited to those specific rights + granted under the terms of a software license agreement between the user + who downloaded the software, his/her employer (which must be your employer) + and Texas Instruments Incorporated (the "License"). You may not use this + Software unless you agree to abide by the terms of the License. The License + limits your use, and you acknowledge, that the Software may not be modified, + copied or distributed unless embedded on a Texas Instruments microcontroller + or used solely and exclusively in conjunction with a Texas Instruments radio + frequency transceiver, which is integrated into your product. Other than for + the foregoing purpose, you may not use, reproduce, copy, prepare derivative + works of, modify, distribute, perform, display or sell this Software and/or + its documentation for any purpose. + + YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE + PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, + INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE, + NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL + TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT, + NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER + LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES + INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE + OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT + OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES + (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS. + + Should you have any questions regarding your right to use this Software, + contact Texas Instruments Incorporated at www.TI.com. +*******************************************************************************/ + +/********************************************************************* + * INCLUDES + */ +#include "inc/utils/util.h" + +#include +#include +#include + +#include +#include + +/********************************************************************* + * TYPEDEFS + */ + +// RTOS queue for profile/app messages. +typedef struct _queueRec_ +{ + Queue_Elem _elem; // queue element + uint8_t *pData; // pointer to app data +} queueRec_t; + +/********************************************************************* + * LOCAL FUNCTIONS + */ + +/******************************************************************************* + * EXTERNAL VARIABLES + */ + +/********************************************************************* + * LOCAL VARIABLES + */ + +/********************************************************************* + * PUBLIC FUNCTIONS + */ + +/********************************************************************* + * @fn Util_constructClock + * + * @brief Initialize a TIRTOS Clock instance. + * + * @param pClock - pointer to clock instance structure. + * @param clockCB - callback function upon clock expiration. + * @param clockDuration - longevity of clock timer in milliseconds + * @param clockPeriod - if set to a value other than 0, the first + * expiry is determined by clockDuration. All + * subsequent expiries use the clockPeriod value. + * @param startFlag - TRUE to start immediately, FALSE to wait. + * @param arg - argument passed to callback function. + * + * @return Clock_Handle - a handle to the clock instance. + */ +Clock_Handle Util_constructClock(Clock_Struct *pClock, + Clock_FuncPtr clockCB, + uint32_t clockDuration, + uint32_t clockPeriod, + uint8_t startFlag, + UArg arg) +{ + Clock_Params clockParams; + + // Convert clockDuration in milliseconds to ticks. + uint32_t clockTicks = clockDuration * (1000 / Clock_tickPeriod); + + // Setup parameters. + Clock_Params_init(&clockParams); + + // Setup argument. + clockParams.arg = arg; + + // If period is 0, this is a one-shot timer. + clockParams.period = clockPeriod * (1000 / Clock_tickPeriod); + + // Starts immediately after construction if true, otherwise wait for a call + // to start. + clockParams.startFlag = startFlag; + + // Initialize clock instance. + Clock_construct(pClock, clockCB, clockTicks, &clockParams); + + return Clock_handle(pClock); +} + +/********************************************************************* + * @fn Util_startClock + * + * @brief Start a clock. + * + * @param pClock - pointer to clock struct + * + * @return none + */ +void Util_startClock(Clock_Struct *pClock) +{ + Clock_Handle handle = Clock_handle(pClock); + + // Start clock instance + Clock_start(handle); +} + +/********************************************************************* + * @fn Util_restartClock + * + * @brief Restart a clock by changing the timeout. + * + * @param pClock - pointer to clock struct + * @param clockTimeout - longevity of clock timer in milliseconds + * + * @return none + */ +void Util_restartClock(Clock_Struct *pClock, uint32_t clockTimeout) +{ + uint32_t clockTicks; + Clock_Handle handle; + + handle = Clock_handle(pClock); + + if (Clock_isActive(handle)) + { + // Stop clock first + Clock_stop(handle); + } + + // Convert timeout in milliseconds to ticks. + clockTicks = clockTimeout * (1000 / Clock_tickPeriod); + + // Set the initial timeout + Clock_setTimeout(handle, clockTicks); + + // Start clock instance + Clock_start(handle); +} + +/********************************************************************* + * @fn Util_isActive + * + * @brief Determine if a clock is currently active. + * + * @param pClock - pointer to clock struct + * + * @return TRUE or FALSE + */ +bool Util_isActive(Clock_Struct *pClock) +{ + Clock_Handle handle = Clock_handle(pClock); + + // Start clock instance + return Clock_isActive(handle); +} + +/********************************************************************* + * @fn Util_stopClock + * + * @brief Stop a clock. + * + * @param pClock - pointer to clock struct + * + * @return none + */ +void Util_stopClock(Clock_Struct *pClock) +{ + Clock_Handle handle = Clock_handle(pClock); + + // Stop clock instance + Clock_stop(handle); +} + +/********************************************************************* + * @fn Util_rescheduleClock + * + * @brief Reschedule a clock by changing the timeout and period values. + * + * @param pClock - pointer to clock struct + * @param clockPeriod - longevity of clock timer in milliseconds + * @return none + */ +void Util_rescheduleClock(Clock_Struct *pClock, uint32_t clockPeriod) +{ + bool running; + uint32_t clockTicks; + Clock_Handle handle; + + handle = Clock_handle(pClock); + running = Clock_isActive(handle); + + if (running) + { + Clock_stop(handle); + } + + // Convert period in milliseconds to ticks. + clockTicks = clockPeriod * (1000 / Clock_tickPeriod); + + Clock_setTimeout(handle, clockTicks); + Clock_setPeriod(handle, clockTicks); + + if (running) + { + Clock_start(handle); + } +} + +/********************************************************************* + * @fn Util_constructQueue + * + * @brief Initialize an RTOS queue to hold messages to be processed. + * + * @param pQueue - pointer to queue instance structure. + * + * @return A queue handle. + */ +Queue_Handle Util_constructQueue(Queue_Struct *pQueue) +{ + // Construct a Queue instance. + Queue_construct(pQueue, NULL); + + return Queue_handle(pQueue); +} + +/********************************************************************* + * @fn Util_enqueueMsg + * + * @brief Creates a queue node and puts the node in RTOS queue. + * + * @param msgQueue - queue handle. + * @param sem - thread's event processing semaphore that queue is + * associated with. + * @param pMsg - pointer to message to be queued + * + * @return TRUE if message was queued, FALSE otherwise. + */ +uint8_t Util_enqueueMsg(Queue_Handle msgQueue, Semaphore_Handle sem, + uint8_t *pMsg) +{ + queueRec_t *pRec; + + // Allocated space for queue node. + + if (pRec = (queueRec_t *)malloc(sizeof(queueRec_t))) + { + pRec->pData = pMsg; + + Queue_enqueue(msgQueue, &pRec->_elem); + + // Wake up the application thread event handler. + if (sem) + { + Semaphore_post(sem); + } + + return TRUE; + } + + // Free the message. + + free(pMsg); + + + return FALSE; +} + +/********************************************************************* + * @fn Util_dequeueMsg + * + * @brief Dequeues the message from the RTOS queue. + * + * @param msgQueue - queue handle. + * + * @return pointer to dequeued message, NULL otherwise. + */ +uint8_t *Util_dequeueMsg(Queue_Handle msgQueue) +{ + if (!Queue_empty(msgQueue)) + { + queueRec_t *pRec = Queue_dequeue(msgQueue); + uint8_t *pData = pRec->pData; + + // Free the queue node + // Note: this does not free space allocated by data within the node. + free(pRec); + + + return pData; + } + + return NULL; +} +