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;
+}
+