vrahane closed pull request #814: charge-control: Adding sensor-like interface 
for battery charge controller ICs
URL: https://github.com/apache/mynewt-core/pull/814
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/hw/charge-control/include/charge-control/charge_control.h 
b/hw/charge-control/include/charge-control/charge_control.h
new file mode 100644
index 000000000..9a8315c2d
--- /dev/null
+++ b/hw/charge-control/include/charge-control/charge_control.h
@@ -0,0 +1,666 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @file charge_control.h
+ * @brief Hardware-agnostic interface for battery charge control ICs
+ *
+ * The Charge Control interface provides a hardware-agnostic layer for driving
+ * battery charge controller ICs.
+ */
+
+#ifndef __CHARGE_CONTROL_H__
+#define __CHARGE_CONTROL_H__
+
+#include "os/os.h"
+#include "os/os_dev.h"
+#include "syscfg/syscfg.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Package init function.  Remove when we have post-kernel init stages.
+ */
+void charge_control_pkg_init(void);
+
+
+/* Forward declaration of charge_control structure. */
+struct charge_control;
+
+/* Comments above intentionally not in Doxygen format */
+
+/* =================================================================
+ * ====================== DEFINES/MACROS ===========================
+ * =================================================================
+ */
+
+/**
+ * Charge control listener constants
+ */
+#define CHARGE_CONTROL_IGN_LISTENER     1
+
+/**
+ * @{ Charge Control API
+ */
+
+/**
+ * Return the OS device structure corresponding to this charge controller
+ */
+#define CHARGE_CONTROL_GET_DEVICE(__c) ((__c)->cc_dev)
+
+/**
+ * Return the interface for this charge controller
+ */
+#define CHARGE_CONTROL_GET_ITF(__c) (&((__c)->cc_itf))
+
+// ---------------------- TYPE -------------------------------------
+
+/** 
+ * Charge controller supported functionality
+ */
+typedef enum {
+    /** No type, used for queries */
+    CHARGE_CONTROL_TYPE_NONE            = (1 << 0),
+    /** Charging status reporting supported */
+    CHARGE_CONTROL_TYPE_STATUS          = (1 << 1),
+    /** Fault reporting supported */
+    CHARGE_CONTROL_TYPE_FAULT           = (1 << 2),
+} charge_control_type_t;
+
+/** 
+ * Possible charge controller states
+ */
+typedef enum {
+    /** Charge controller is disabled (if enable/disable function exists) */
+    CHARGE_CONTROL_STATUS_DISABLED      = 0,
+    /** No charge source is present at the charge controller input */
+    CHARGE_CONTROL_STATUS_NO_SOURCE,
+    /** Charge controller is charging a battery */
+    CHARGE_CONTROL_STATUS_CHARGING,
+    /** Charge controller has completed its charging cycle */
+    CHARGE_CONTROL_STATUS_CHARGE_COMPLETE,
+    /** Charging is temporarily suspended */
+    CHARGE_CONTROL_STATUS_SUSPEND,
+    /** Charge controller has detected a fault condition */
+    CHARGE_CONTROL_STATUS_FAULT,
+    /** Unspecified status; User must understand how to interpret */
+    CHARGE_CONTROL_STATUS_OTHER,
+} charge_control_status_t;
+
+/**
+ * Possible fault conditions for the charge controller
+ */
+typedef enum {
+    /** No fault detected */
+    CHARGE_CONTROL_FAULT_NONE           = 0,
+    /** Charge controller input voltage exceeds threshold */
+    CHARGE_CONTROL_FAULT_OV             = (1 << 0),
+    /** Charge controller input voltage below required operating level */
+    CHARGE_CONTROL_FAULT_UV             = (1 << 1),
+    /** Charge controller is not running at its programmed charging current */
+    CHARGE_CONTROL_FAULT_ILIM           = (1 << 2),
+    /** Charge controller detected an over-temperature condition */
+    CHARGE_CONTROL_FAULT_THERM          = (1 << 3),
+    /** Unspecified fault; User must understand how to interpret */
+    CHARGE_CONTROL_FAULT_OTHER          = (1 << 4),
+} charge_control_fault_t;
+
+/**
+ * Charge controller type traits list.  Allows a certain type of charge control
+ * data to be polled at n times the normal poll rate.
+ */
+struct charge_control_type_traits {
+    /** The type of charge control data to which the traits apply */
+    charge_control_type_t cctt_charge_control_type;
+
+    /** Poll rate multiple */
+    uint16_t cctt_poll_n;
+
+    /** Polls left until the charge control type is polled */
+    uint16_t cctt_polls_left;
+
+    /** Next item in the traits list. The head of this list is contained
+     * within the charge control object
+     */
+    SLIST_ENTRY(charge_control_type_traits) cctt_next;
+};
+
+// ---------------------- CONFIG -----------------------------------
+
+/**
+ * Configuration structure, describing a specific charge controller type off of
+ * an existing charge controller.
+ */
+struct charge_control_cfg {
+    /** Reserved for future use */
+    uint8_t _reserved[4];
+};
+
+// ---------------------- INTERFACE --------------------------------
+
+/**
+ * Charge control serial interface types
+ */
+enum charge_control_itf_type {
+    CHARGE_CONTROL_ITF_SPI      =   0,
+    CHARGE_CONTROL_ITF_I2C      =   1,
+    CHARGE_CONTROL_ITF_UART     =   2,
+};
+
+/**
+ * Specifies a serial interface to use for communication with the charge
+ * controller (if applicable)
+ */
+struct charge_control_itf {
+    /* Charge control interface type */
+    enum charge_control_itf_type cci_type;
+
+    /* Charge control interface number (i.e. 0 for I2C0, 1 for I2C1) */
+    uint8_t cci_num;
+
+    /* Charge control CS pin (for SPI interface type) */
+    uint8_t cci_cs_pin;
+
+    /* Charge control interface address (for I2C interface type) */
+    uint16_t cci_addr;
+};
+
+// ---------------------- LISTENER ---------------------------------
+
+/**
+ * Callback for handling charge controller status.
+ *
+ * @param The charge control object for which data is being returned
+ * @param The argument provided to charge_control_read() function.
+ * @param A single charge control reading for that listener
+ * @param The type of charge control reading for the data function
+ *
+ * @return 0 on success, non-zero error code on failure.
+ */
+typedef int (*charge_control_data_func_t)(struct charge_control *, void *,
+        void *, charge_control_type_t);
+
+/**
+ * Listener structure which may be registered to a charge controller. The
+ * listener will call the supplied charge_control_data_func_t every time it
+ * reads new data of the specified type.
+ */
+struct charge_control_listener {
+    /* The type of charge control data to listen for, this is interpreted as a
+     * mask, and this listener is called for all charge control types that
+     * match the mask.
+     */
+    charge_control_type_t ccl_type;
+
+    /* Charge control data handler function, called when has data */
+    charge_control_data_func_t ccl_func;
+
+    /* Argument for the charge control listener */
+    void *ccl_arg;
+
+    /* Next item in the charge control listener list.  The head of this list is
+     * contained within the charge control object.
+     */
+    SLIST_ENTRY(charge_control_listener) ccl_next;
+};
+
+/* ---------------------- DRIVER FUNCTIONS ------------------------- */
+
+/**
+ * Read from a charge controller, given a specific type of information to read
+ * (e.g. CHARGE_CONTROL_TYPE_STATUS).
+ *
+ * @param The charge controller to read from
+ * @param The type(s) of charge control value(s) to read, interpreted as a mask
+ * @param The function to call with each value read.  If NULL, it calls all
+ *        charge control listeners associated with this function.
+ * @param The argument to pass to the read callback.
+ * @param Timeout.  If block until result, specify OS_TIMEOUT_NEVER, 0 returns
+ *        immediately (no wait.)
+ *
+ * @return 0 on success, non-zero error code on failure.
+ */
+typedef int (*charge_control_read_func_t)(struct charge_control *,
+        charge_control_type_t, charge_control_data_func_t, void *, uint32_t);
+
+/**
+ * Get the configuration of the charge controller.  This includes the value
+ * type of the charge controller.
+ *
+ * @param The desired charge controller
+ * @param The type of charge control value to get configuration for
+ * @param A pointer to the charge control value to place the returned result
+ *
+ * @return 0 on success, non-zero error code on failure.
+ */
+typedef int (*charge_control_get_config_func_t)(struct charge_control *,
+        charge_control_type_t, struct charge_control_cfg *);
+
+/** 
+ * Reconfigure the settings of a charge controller.
+ *
+ * @param ptr to the charge controller-specific stucture
+ * @param ptr to the charge controller-specific configuration structure
+ *
+ * @return 0 on success, non-zero error code on failure.
+ */
+typedef int (*charge_control_set_config_func_t)(struct charge_control *,
+        void *);
+
+/**
+ * Read the status of a charge controller.
+ *
+ * @param ptr to the charge controller-specific structure
+ * @param ptr to the location where the charge control status will be stored
+ *
+ * @return 0 on success, non-zero error code on failure.
+ */
+typedef int (*charge_control_get_status_func_t)(struct charge_control *, int 
*);
+
+/**
+ * Read the fault status of a charge controller.
+ *
+ * @param ptr to the charge controller-specific structure
+ * @param ptr to the location where the fault status will be stored
+ *
+ * @return 0 on success, non-zero error code on failure.
+ */
+typedef int (*charge_control_get_fault_func_t)(struct charge_control *,
+        charge_control_fault_t *);
+
+/**
+ * Enable a charge controller
+ *
+ * @param The charge controller to enable
+ *
+ * @return 0 on success, non-zero error code on failure.
+ */
+typedef int (*charge_control_enable_func_t)(struct charge_control *);
+
+/**
+ * Disable a charge controller
+ *
+ * @param The charge controller to disable
+ *
+ * @return 0 on success, non-zero error code on failure
+ */
+typedef int (*charge_control_disable_func_t)(struct charge_control *);
+
+/** 
+ * Pointers to charge controller-specific driver functions.  
+ */
+struct charge_control_driver {
+    charge_control_read_func_t          ccd_read;
+    charge_control_get_config_func_t    ccd_get_config;
+    charge_control_set_config_func_t    ccd_set_config;
+    charge_control_get_status_func_t    ccd_get_status;
+    charge_control_get_fault_func_t     ccd_get_fault;
+    charge_control_enable_func_t        ccd_enable;
+    charge_control_disable_func_t       ccd_disable;
+};
+
+// ---------------------- POLL RATE CONTROL ------------------------
+
+struct charge_control_timestamp {
+    struct os_timeval cct_ostv;
+    struct os_timezone cct_ostz;
+    uint32_t cct_cputime;
+};
+
+// ---------------------- CHARGE CONTROL OBJECT --------------------
+
+struct charge_control {
+    /* The OS device this charge controller inherits from, this is typically a
+     * charge controller specific driver.
+     */
+    struct os_dev *cc_dev;
+
+    /* The lock for this charge controller object */
+    struct os_mutex cc_lock;
+
+
+    /* A bit mask describing information types available from this charge
+     * controller.  If the bit corresponding to the charge_control_type_t is
+     * set, then this charge controller supports that type of information.
+     */
+    charge_control_type_t cc_types;
+
+    /* Charge control information type mask.  A driver for a charge controller
+     * or an implementation of the controller may be written to support some or
+     * all of the types of information */
+    charge_control_type_t cc_mask;
+
+    /**
+     * Poll rate in MS for this charge controller.
+     */
+    uint32_t cc_poll_rate;
+
+    /* The next time at which we will poll data from this charge controller */
+    os_time_t cc_next_run;
+
+    /* Charge controller driver specific functions, created by the device
+     * registering the charge controller.
+     */
+    struct charge_control_driver *cc_funcs;
+
+    /* Charge controller last reading timestamp */
+    struct charge_control_timestamp cc_sts;
+
+    /* Charge controller interface structure */
+    struct charge_control_itf cc_itf;
+
+    /* A list of listeners that are registered to receive data from this
+     * charge controller
+     */
+    SLIST_HEAD(, charge_control_listener) cc_listener_list;
+
+    /** A list of traits registered to the charge control data types */
+    SLIST_HEAD(, charge_control_type_traits) cc_type_traits_list;
+
+    /* The next charge controller in the global charge controller list. */
+    SLIST_ENTRY(charge_control) cc_next;
+};
+
+/* =================================================================
+ * ====================== CHARGE CONTROL ===========================
+ * =================================================================
+
+/**
+ * Initialize charge control structure data and mutex and associate it with an
+ * os_dev.
+ *
+ * @param Charge control struct to initialize
+ * @param os_dev to associate with the charge control struct
+ *
+ * @return 0 on success, non-zero error code on failure.
+ */
+int charge_control_init(struct charge_control *, struct os_dev *);
+
+/**
+ * Register a charge control listener. This allows a calling application to
+ * receive callbacks for data from a given charge control object.
+ *
+ * For more information on the type of callbacks available, see the 
documentation
+ * for the charge control listener structure.
+ *
+ * @param The charge controller to register a listener on
+ * @param The listener to register onto the charge controller
+ *
+ * @return 0 on success, non-zero error code on failure.
+ */
+int charge_control_register_listener(struct charge_control *,
+        struct charge_control_listener *);
+
+/**
+ * Un-register a charge control listener. This allows a calling application to
+ * unset callbacks for a given charge control object.
+ *
+ * @param The charge control object
+ * @param The listener to remove from the charge control listener list
+ *
+ * @return 0 on success, non-zero error code on failure.
+ */
+int charge_control_unregister_listener(struct charge_control *,
+        struct charge_control_listener *);
+
+
+/**
+ * Read from a charge controller, given a specific type of information to read
+ * (e.g. CHARGE_CONTROL_TYPE_STATUS).
+ *
+ * @param The charge controller to read from
+ * @param The type(s) of charge control value(s) to read, interpreted as a mask
+ * @param The function to call with each value read.  If NULL, it calls all
+ *        charge control listeners associated with this function.
+ * @param The argument to pass to the read callback.
+ * @param Timeout.  If block until result, specify OS_TIMEOUT_NEVER, 0 returns
+ *        immediately (no wait.)
+ *
+ * @return 0 on success, non-zero error code on failure.
+ */
+int charge_control_read(struct charge_control *, charge_control_type_t, 
+        charge_control_data_func_t, void *, uint32_t);
+
+/**
+ * Set the charge controller poll rate
+ *
+ * @param The name of the charge controller
+ * @param The poll rate in milli seconds
+ */
+int
+charge_control_set_poll_rate_ms(char *, uint32_t);
+
+/**
+ * Set a charge control type to poll at some multiple of the poll rate
+ *
+ * @param The name of the charge controller
+ * @param The charge control type
+ * @param The multiple of the poll rate
+ */
+int 
+charge_control_set_n_poll_rate(char *, struct charge_control_type_traits *);
+
+/**
+ * Set the driver functions for this charge controller, along with the type of
+ * data available for the given charge controller.
+ *
+ * @param The charge controller to set the driver information for
+ * @param The types of charge control data available
+ * @param The driver functions for this charge controller
+ *
+ * @return 0 on success, non-zero error code on failure
+ */
+static inline int
+charge_control_set_driver(struct charge_control *cc, charge_control_type_t 
type,
+        struct charge_control_driver *driver)
+{
+    cc->cc_funcs = driver;
+    cc->cc_types = type;
+
+    return (0);
+}
+
+/**
+ * Set the charge control driver mask so that the developer who configures the
+ * charge controller tells the charge control framework which data types to 
+ * send back to the user
+ *
+ * @param The charge controller to set the mask for
+ * @param The mask
+ */
+static inline int
+charge_control_set_type_mask(struct charge_control *cc,
+        charge_control_type_t mask)
+{
+    cc->cc_mask = mask;
+
+    return (0);
+}
+
+/**
+ * Check if the given type is supported by the charge control device
+ *
+ * @param charge control object
+ * @param Type to be checked
+ *
+ * @return type bitfield, if supported, 0 if not supported
+ */
+static inline charge_control_type_t
+charge_control_check_type(struct charge_control *cc,
+        charge_control_type_t type)
+{
+    return (cc->cc_types & cc->cc_mask & type);
+}
+
+/**
+ * Set interface type and number
+ *
+ * @param The charge control object to set the interface for
+ * @param The interface type to set
+ * @param The interface number to set
+ */
+static inline int
+charge_control_set_interface(struct charge_control *cc,
+        struct charge_control_itf *itf)
+{
+    cc->cc_itf.cci_type = itf->cci_type;
+    cc->cc_itf.cci_num = itf->cci_num;
+    cc->cc_itf.cci_cs_pin = itf->cci_cs_pin;
+    cc->cc_itf.cci_addr = itf->cci_addr;
+
+    return (0);
+}
+
+/**
+ * Read the configuration for the charge control type "type," and return the
+ * configuration into "cfg."
+ *
+ * @param The charge control to read configuration for
+ * @param The type of charge control configuration to read
+ * @param The configuration structure to point to.
+ *
+ * @return 0 on success, non-zero error code on failure.
+ */
+static inline int
+charge_control_get_config(struct charge_control *cc,
+        charge_control_type_t type, struct charge_control_cfg *cfg)
+{
+    return (cc->cc_funcs->ccd_get_config(cc, type, cfg));
+}
+
+// =================================================================
+// ====================== CHARGE CONTROL MANAGER ===================
+// =================================================================
+
+/**
+ * @{ Charge Control Manager API
+ */
+
+/**
+ * Registers a charge controller with the charge control manager list.  This
+ * makes the charge controller externally searchable.
+ *
+ * @param The charge controller to register
+ *
+ * @return 0 on success, non-zero error code on failure.
+ */
+int charge_control_mgr_register(struct charge_control *);
+
+/**
+ * Return the charge control event queue. 
+ */
+struct os_eventq *charge_control_mgr_evq_get(void);
+
+
+typedef int (*charge_control_mgr_compare_func_t)(struct charge_control *,
+        void *);
+
+/**
+ * The charge control manager contains a list of charge controllers, this
+ * function returns the next charge controller in that list, for which
+ * compare_func() returns successful (one).  If prev_cursor is provided, the
+ * function starts at that point in the charge controller list.
+ *
+ * @warn This function MUST be locked by charge_control_mgr_lock/unlock() if
+ * the goal is to iterate through charge controllers (as opposed to just
+ * finding one.)  As the "prev_cursor" may be resorted in the charge control
+ * list, in between calls.
+ *
+ * @param The comparison function to use against charge controllers in the 
list.
+ * @param The argument to provide to that comparison function
+ * @param The previous charge controller in the manager list, in case of
+ *        iteration.  If desire is to find first matching charge controller,
+ *        provide a NULL value.
+ *
+ * @return A pointer to the first charge controller found from prev_cursor, or
+ *         NULL, if none found.
+ *
+ */
+struct charge_control *charge_control_mgr_find_next(
+        charge_control_mgr_compare_func_t, void *, struct charge_control *);
+
+/**
+ * Find the "next" charge controller available for a giventype.
+ *
+ * If the charge control parameter is present find the next entry from that
+ * parameter.  Otherwise, return the first match.
+ *
+ * @param The type of charge controller to search for
+ * @param The cursor to search from, or NULL to start from the beginning.
+ *
+ * @return A pointer to the charge control object matching that type, or NULL 
if
+ *         none found.
+ */
+struct charge_control *charge_control_mgr_find_next_bytype(
+        charge_control_type_t, struct charge_control *);
+
+/**
+ * Search the charge control list and find the next charge controller that 
+ * corresponds to a given device name.
+ *
+ * @param The device name to search for
+ * @param The previous charge controller found with this device name
+ *
+ * @return 0 on success, non-zero error code on failure
+ */
+struct charge_control *charge_control_mgr_find_next_bydevname(char *, 
+        struct charge_control *);
+
+/**
+ * Check if charge control type matches
+ *
+ * @param The charge control object
+ * @param The type to check
+ *
+ * @return 1 if matches, 0 if it doesn't match.
+ */
+int charge_control_mgr_match_bytype(struct charge_control *, void *);
+
+/**
+ * Puts read event on the charge control manager evq
+ *
+ * @param arg
+ */
+void
+charge_control_mgr_put_read_evt(void *);
+
+#if MYNEWT_VAL(CHARGE_CONTROL_CLI)
+char*
+charge_control_ftostr(float, char *, int);
+#endif
+
+/**
+ * }@
+ */
+
+/* End Charge Control Manager API */
+
+
+/**
+ * @}
+ */
+
+/* End Charge Control API */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CHARGE_CONTROL_H__ */
diff --git a/hw/charge-control/pkg.yml b/hw/charge-control/pkg.yml
new file mode 100644
index 000000000..355de704d
--- /dev/null
+++ b/hw/charge-control/pkg.yml
@@ -0,0 +1,36 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+pkg.name: hw/charge-control
+pkg.description: Battery Charge Controller IC Interface
+pkg.keywords:
+
+pkg.deps:
+    - kernel/os
+
+pkg.deps.CHARGE_CONTROL_CLI:
+    - sys/shell
+    - util/parse
+
+pkg.req_apis:
+    - console
+
+pkg.init:
+    charge_control_pkg_init: 501
+
diff --git a/hw/charge-control/src/charge_control.c 
b/hw/charge-control/src/charge_control.c
new file mode 100644
index 000000000..4b153e88c
--- /dev/null
+++ b/hw/charge-control/src/charge_control.c
@@ -0,0 +1,1039 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <string.h>
+
+#include "os/os.h"
+#include "os/os_dev.h"
+#include "defs/error.h"
+#include "syscfg/syscfg.h"
+#include "sysinit/sysinit.h"
+#include "charge-control/charge_control.h"
+#include "charge_control_priv.h"
+
+/* =================================================================
+ * ====================== LOCAL FUNCTION DECLARATIONS ==============
+ * =================================================================
+ */
+
+/* ---------------------------- OS -------------------------------- */
+
+static void charge_control_read_ev_cb(struct os_event *ev);
+
+static void charge_control_mgr_wakeup_event(struct os_event *);
+static void charge_control_base_ts_update_event(struct os_event *);
+
+/* ---------------------- CHARGE CONTROL --------------------------- */
+
+/**
+ * Prevents changes to a charge control device during an operation
+ *
+ * @param Charge control device to lock
+ *
+ * @return 0 on success, non-zero error code on failure
+ */
+static int charge_control_lock(struct charge_control *);
+
+/**
+ * Releases mutex lock on charge control device
+ *
+ * @param Charge control device to release
+ */
+static void charge_control_unlock(struct charge_control *);
+
+/**
+ * Thread-safe operation to change poll rate on charge control device
+ */
+static void
+charge_control_update_poll_rate(struct charge_control *, uint32_t);
+
+static os_time_t
+charge_control_calc_nextrun_delta(struct charge_control *, os_time_t);
+
+static struct charge_control *
+charge_control_find_min_nextrun(os_time_t, os_time_t *);
+
+static void
+charge_control_update_nextrun(struct charge_control *, os_time_t);
+
+static int
+charge_control_read_data_func(struct charge_control *, void *, void *,
+        charge_control_type_t);
+
+static void
+charge_control_up_timestamp(struct charge_control *);
+
+static int
+charge_control_insert_type_trait(struct charge_control *,
+        struct charge_control_type_traits *);
+
+static int
+charge_control_remove_type_trait(struct charge_control *,
+        struct charge_control_type_traits *);
+
+/**
+ * Search the charge control type traits list for a specific type of charge
+ * controller.  Set the charge control pointer to NULL to start from the
+ * beginning of the list or to a specific charge control object to begin from
+ * that point in the list.
+ *
+ * @param The charge controller type to search for
+ * @param Pointer to a charge controller
+ *
+ * @return NULL when no charge control type is found, pointer to a
+ * charge_control_type_traits structure when found.
+ */
+static struct charge_control_type_traits *
+charge_control_get_type_traits_bytype(charge_control_type_t,
+        struct charge_control *);
+
+static struct charge_control *
+charge_control_get_type_traits_byname(char *,
+        struct charge_control_type_traits **, charge_control_type_t);
+
+static uint8_t
+charge_control_type_traits_empty(struct charge_control *);
+
+static void
+charge_control_poll_per_type_trait(struct charge_control *, os_time_t,
+        os_time_t);
+
+/* ---------------------- CHARGE CONTROL MANAGER ------------------- */
+
+/**
+ * Pends on the charge control manager mutex indefinitely.  Must be used prior
+ * to operations which iterate through the list of charge controllers.
+ *
+ * @return 0 on success, non-zero error code on failure.
+ */
+static int
+charge_control_mgr_lock(void);
+
+/** Releases the charge control manager mutex. */
+static void
+charge_control_mgr_unlock(void);
+
+static int
+charge_control_mgr_match_bydevname(struct charge_control *, void *arg);
+
+static void
+charge_control_mgr_remove(struct charge_control *);
+
+static void
+charge_control_mgr_insert(struct charge_control *);
+
+static void
+charge_control_mgr_poll_bytype(struct charge_control *, charge_control_type_t,
+        struct charge_control_type_traits *, os_time_t);
+
+static void
+charge_control_mgr_evq_set(struct os_eventq *);
+
+static void
+charge_control_mgr_init(void);
+
+/* =================================================================
+ * ====================== LOCAL STRUCTS/VARIABLES ==================
+ * =================================================================
+ */
+
+/**
+ * Declaration and definition of manager for the list of charge control devices
+ */
+struct {
+    /** Locks list for manipulation/search operations */
+    struct os_mutex mgr_lock;
+
+    /** Manages poll rates of the charge control devices */
+    struct os_callout mgr_wakeup_callout;
+
+    /** Event queue to which wakeup events are added */
+    struct os_eventq *mgr_eventq;
+
+    /** Charge control device list */
+    SLIST_HEAD(, charge_control) mgr_charge_control_list;
+} charge_control_mgr;
+
+struct charge_control_read_ctx {
+    charge_control_data_func_t user_func;
+    void *user_arg;
+};
+
+struct charge_control_timestamp charge_control_base_ts;
+
+/** OS Callout to update charge control timestamp */
+struct os_callout cct_up_osco;
+
+/** Event for performing a charge control read */
+static struct os_event charge_control_read_event = {
+    .ev_cb = charge_control_read_ev_cb,
+};
+
+/* =================================================================
+ * ====================== PKG ======================================
+ * =================================================================
+ */
+
+void charge_control_pkg_init(void)
+{
+    /* Ensure this function only gets called by sysinit. */
+    SYSINIT_ASSERT_ACTIVE();
+
+    charge_control_mgr_init();
+
+#if MYNEWT_VAL(CHARGE_CONTROL_CLI)
+    charge_control_shell_register();
+#endif
+}
+
+/* =================================================================
+ * ====================== OS =======================================
+ * =================================================================
+ */
+
+static void
+charge_control_read_ev_cb(struct os_event *ev)
+{
+    int rc;
+    struct charge_control_read_ev_ctx *ccrec;
+
+    ccrec = ev->ev_arg;
+    rc = charge_control_read(ccrec->ccrec_charge_control, ccrec->ccrec_type, 
+            NULL, NULL, OS_TIMEOUT_NEVER);
+    assert(rc == 0);
+}
+
+static void
+charge_control_mgr_wakeup_event(struct os_event *ev)
+{
+    struct charge_control *cursor;
+    os_time_t now;
+    os_time_t next_wakeup;
+
+    now = os_time_get();
+
+#if MYNEWT_VAL(SENSOR_POLL_TEST_LOG)
+    ccmgr_wakeup[ccmgr_wakeup_idx++%500] = now;
+#endif
+
+    charge_control_mgr_lock();
+
+    cursor = NULL;
+    while (1) {
+
+        cursor = charge_control_find_min_nextrun(now, &next_wakeup);
+
+        charge_control_lock(cursor);
+        /* Charge controllers that are not periodic are inserted at the end of
+         * the charge_control list.
+         */
+        if (!cursor->cc_poll_rate) {
+            charge_control_unlock(cursor);
+            charge_control_mgr_unlock();
+            return;
+        }
+
+        /* List is sorted by what runs first.  If we reached the first element
+         * that doesn't run, break out.
+         */
+        if (next_wakeup > 0) {
+            break;
+        }
+
+        if (charge_control_type_traits_empty(cursor)) {
+
+            charge_control_mgr_poll_bytype(cursor, cursor->cc_mask, NULL, now);
+        } else {
+            charge_control_poll_per_type_trait(cursor, now, next_wakeup);
+        }
+
+        charge_control_update_nextrun(cursor, now);
+
+        charge_control_unlock(cursor);
+    }
+
+    charge_control_mgr_unlock();
+
+    os_callout_reset(&charge_control_mgr.mgr_wakeup_callout, next_wakeup);
+}
+
+static void
+charge_control_base_ts_update_event(struct os_event *ev)
+{
+    os_time_t ticks;
+    int rc;
+    struct os_timeval ostv;
+    struct os_timezone ostz;
+
+    ticks = os_time_get();
+
+    rc = os_gettimeofday(&ostv, &ostz);
+    if (rc) {
+        /* There is nothing we can do here
+         * just reset the timer frequently if we
+         * fail to get time, till then we will keep using
+         * old timestamp values.
+         */
+        ticks += OS_TICKS_PER_SEC * 600;
+        goto done;
+    }
+
+    /* CPU time gets wrapped in 4295 seconds since it is uint32_t,
+     * hence the hardcoded value of 3600 seconds, We want to make
+     * sure that the cputime never gets wrapped more than once.
+     * os_timeval usecs value gets wrapped in 2147 secs since it is int32_t.
+     * Hence, we take 2000 secs so that we update before it gets wrapped
+     * without cutting it too close.
+     */
+    ticks += OS_TICKS_PER_SEC * 2000;
+
+    charge_control_base_ts.cct_ostv = ostv;
+    charge_control_base_ts.cct_ostz = ostz;
+    charge_control_base_ts.cct_cputime = os_cputime_get32();
+
+done:
+    os_callout_reset(&cct_up_osco, ticks);
+}
+
+/* =================================================================
+ * ====================== CHARGE CONTROL ===========================
+ * =================================================================
+ */
+
+static int
+charge_control_lock(struct charge_control *cc)
+{
+    int rc;
+
+    rc = os_mutex_pend(&cc->cc_lock, OS_TIMEOUT_NEVER);
+    if (rc == 0 || rc == OS_NOT_STARTED) {
+        return (0);
+    }
+    return (rc);
+}
+
+static void
+charge_control_unlock(struct charge_control *cc)
+{
+    os_mutex_release(&cc->cc_lock);
+}
+
+static void
+charge_control_update_poll_rate(struct charge_control * cc, uint32_t poll_rate)
+{
+    charge_control_lock(cc);
+
+    cc->cc_poll_rate = poll_rate;
+
+    charge_control_unlock(cc);
+}
+
+static os_time_t
+charge_control_calc_nextrun_delta(struct charge_control * cc, os_time_t now)
+{
+    os_time_t charge_control_ticks;
+    int delta;
+
+    charge_control_lock(cc);
+
+    delta = (int32_t)(cc->cc_next_run - now);
+    if (delta < 0) {
+        /* This fires the callout right away */
+        charge_control_ticks = 0;
+    } else {
+        charge_control_ticks = delta;
+    }
+
+    charge_control_unlock(cc);
+
+    return charge_control_ticks;
+}
+
+static struct charge_control *
+charge_control_find_min_nextrun(os_time_t now, os_time_t *min_nextrun)
+{
+    struct charge_control *head;
+
+    head = NULL;
+
+    charge_control_mgr_lock();
+
+    head = SLIST_FIRST(&charge_control_mgr.mgr_charge_control_list);
+
+    *min_nextrun = charge_control_calc_nextrun_delta(head, now);
+
+    charge_control_mgr_unlock();
+
+    return head;
+}
+
+static void
+charge_control_update_nextrun(struct charge_control *cc, os_time_t now)
+{
+    os_time_t charge_control_ticks;
+
+    os_time_ms_to_ticks(cc->cc_poll_rate, &charge_control_ticks);
+
+    charge_control_lock(cc);
+
+    /* Remove the charge_control from the charge_control list for insert. */
+    charge_control_mgr_remove(cc);
+
+    /* Set next wakeup, and insert the charge_control back into the
+     * list.
+     */
+    cc->cc_next_run = charge_control_ticks + now;
+
+    /* Re-insert the charge_control manager, with the new wakeup time. */
+    charge_control_mgr_insert(cc);
+
+    charge_control_unlock(cc);
+}
+
+static int
+charge_control_read_data_func(struct charge_control *cc, void *arg,
+        void *data, charge_control_type_t type)
+{
+    struct charge_control_listener *listener;
+    struct charge_control_read_ctx *ctx;
+
+    ctx = (struct charge_control_read_ctx *) arg;
+
+    if ((uint8_t)(uintptr_t)(ctx->user_arg) != CHARGE_CONTROL_IGN_LISTENER) {
+        /* Notify all listeners first */
+        SLIST_FOREACH(listener, &cc->cc_listener_list, ccl_next) {
+            if (listener->ccl_type & type) {
+                listener->ccl_func(cc, listener->ccl_arg, data, type);
+            }
+        }
+    }
+
+    /* Call data function */
+    if (ctx->user_func != NULL) {
+        return (ctx->user_func(cc, ctx->user_arg, data, type));
+    } else {
+        return (0);
+    }
+}
+
+static void charge_control_up_timestamp(struct charge_control *cc)
+{
+    uint32_t curr_ts_ticks;
+    uint32_t ts;
+
+    curr_ts_ticks = os_cputime_get32();
+
+    ts = os_cputime_ticks_to_usecs(curr_ts_ticks -
+             charge_control_base_ts.cct_cputime);
+
+    /* Updating cputime */
+    charge_control_base_ts.cct_cputime = cc->cc_sts.cct_cputime = 
curr_ts_ticks;
+
+    /* Updating seconds */
+    charge_control_base_ts.cct_ostv.tv_sec  =
+        charge_control_base_ts.cct_ostv.tv_sec
+        + (ts + charge_control_base_ts.cct_ostv.tv_usec)/1000000;
+    cc->cc_sts.cct_ostv.tv_sec = charge_control_base_ts.cct_ostv.tv_sec;
+
+    /* Updating Micro seconds */
+    charge_control_base_ts.cct_ostv.tv_usec  =
+        (charge_control_base_ts.cct_ostv.tv_usec + ts)%1000000;
+    cc->cc_sts.cct_ostv.tv_usec = charge_control_base_ts.cct_ostv.tv_usec;
+}
+
+static int
+charge_control_insert_type_trait(struct charge_control *cc,
+        struct charge_control_type_traits *cctt)
+{
+    struct charge_control_type_traits *cursor, *prev;
+    int rc;
+
+    rc = charge_control_lock(cc);
+    if (rc != 0) {
+        goto err;
+    }
+
+    prev = cursor = NULL;
+    SLIST_FOREACH(cursor, &cc->cc_type_traits_list, cctt_next) {
+        if (cursor->cctt_poll_n == 0) {
+            break;
+        }
+
+        if (OS_TIME_TICK_LT(cctt->cctt_poll_n, cursor->cctt_poll_n)) {
+            break;
+        }
+
+        prev = cursor;
+    }
+
+    if (prev == NULL) {
+        SLIST_INSERT_HEAD(&cc->cc_type_traits_list, cctt, cctt_next);
+    }
+    else {
+        SLIST_INSERT_AFTER(prev, cctt, cctt_next);
+    }
+
+    charge_control_unlock(cc);
+
+    return 0;
+err:
+    return rc;
+}
+
+static int
+charge_control_remove_type_trait(struct charge_control *cc,
+        struct charge_control_type_traits *cctt)
+{
+    int rc;
+
+    rc = charge_control_lock(cc);
+    if (rc != 0) {
+        goto err;
+    }
+
+    /* Remove this entry from the list */
+    SLIST_REMOVE(&cc->cc_type_traits_list, cctt, charge_control_type_traits,
+            cctt_next);
+
+    charge_control_unlock(cc);
+
+    return (0);
+err:
+    return (rc);
+}
+
+static struct charge_control_type_traits *
+charge_control_get_type_traits_bytype(charge_control_type_t type,
+        struct charge_control * cc)
+{
+    struct charge_control_type_traits *cctt;
+
+    cctt = NULL;
+
+    charge_control_lock(cc);
+
+    SLIST_FOREACH(cctt, &cc->cc_type_traits_list, cctt_next) {
+        if (cctt->cctt_charge_control_type == type) {
+            break;
+        }
+    }
+
+    charge_control_unlock(cc);
+
+    return cctt;
+}
+
+static struct charge_control *
+charge_control_get_type_traits_byname(char *devname,
+        struct charge_control_type_traits **cctt, charge_control_type_t type)
+{
+    struct charge_control *cc;
+
+    cc = charge_control_mgr_find_next_bydevname(devname, NULL);
+    if (!cc) {
+        goto err;
+    }
+
+    *cctt = charge_control_get_type_traits_bytype(type, cc);
+
+err:
+    return cc;
+}
+
+static uint8_t
+charge_control_type_traits_empty(struct charge_control * cc)
+{
+    return SLIST_EMPTY(&cc->cc_type_traits_list);
+}
+
+static void
+charge_control_poll_per_type_trait(struct charge_control *cc, os_time_t now,
+        os_time_t next_wakeup)
+{
+    struct charge_control_type_traits *cctt;
+
+    /* Lock the charge controller */
+    charge_control_lock(cc);
+
+    SLIST_FOREACH(cctt, &cc->cc_type_traits_list, cctt_next) {
+
+        /* poll multiple is one if no multiple is specified,
+         * as a result, the charge controller would get polled at the
+         * poll rate if no multiple is specified
+         *
+         * If a multiple is specified, the charge controller would get polled
+         * at the poll multiple
+         */
+
+        charge_control_mgr_poll_bytype(cc, cctt->cctt_charge_control_type, 
cctt,
+                               now);
+    }
+
+    /* Unlock the charge controller to allow other access */
+    charge_control_unlock(cc);
+}
+
+int
+charge_control_init(struct charge_control *cc, struct os_dev *dev)
+{
+    int rc;
+
+    memset(cc, 0, sizeof(*cc));
+
+    rc = os_mutex_init(&cc->cc_lock);
+    if (rc != 0) {
+        goto err;
+    }
+    cc->cc_dev = dev;
+
+    return (0);
+err:
+    return (rc);
+}
+
+int
+charge_control_register_listener(struct charge_control *cc,
+        struct charge_control_listener *listener)
+{
+    int rc;
+
+    rc = charge_control_lock(cc);
+    if (rc != 0) {
+        goto err;
+    }
+
+    SLIST_INSERT_HEAD(&cc->cc_listener_list, listener, ccl_next);
+
+    charge_control_unlock(cc);
+
+    return (0);
+err:
+    return (rc);
+}
+
+int
+charge_control_unregister_listener(struct charge_control *cc,
+        struct charge_control_listener *listener)
+{
+    int rc;
+
+    rc = charge_control_lock(cc);
+    if (rc != 0) {
+        goto err;
+    }
+
+    /* Remove this entry from the list */
+    SLIST_REMOVE(&cc->cc_listener_list, listener, charge_control_listener,
+            ccl_next);
+
+    charge_control_unlock(cc);
+
+    return (0);
+err:
+    return (rc);
+}
+
+int
+charge_control_read(struct charge_control *cc, charge_control_type_t type,
+        charge_control_data_func_t data_func, void *arg, uint32_t timeout)
+{
+    struct charge_control_read_ctx ccrc;
+    int rc;
+
+    rc = charge_control_lock(cc);
+    if (rc) {
+        goto err;
+    }
+
+    ccrc.user_func = data_func;
+    ccrc.user_arg = arg;
+
+    if (!charge_control_mgr_match_bytype(cc, (void *)&type)) {
+        rc = SYS_ENOENT;
+        goto err;
+    }
+
+    charge_control_up_timestamp(cc);
+
+    rc = cc->cc_funcs->ccd_read(cc, type, charge_control_read_data_func, &ccrc,
+            timeout);
+    if (rc) {
+        goto err;
+    }
+
+err:
+    charge_control_unlock(cc);
+    return (rc);
+}
+
+/* =================================================================
+ * ====================== CHARGE CONTROL MANAGER ===================
+ * =================================================================
+ */
+
+static int
+charge_control_mgr_lock(void)
+{
+    int rc;
+
+    rc = os_mutex_pend(&charge_control_mgr.mgr_lock, OS_TIMEOUT_NEVER);
+    if (rc == 0 || rc == OS_NOT_STARTED) {
+        return (0);
+    }
+    return rc;
+}
+
+static void
+charge_control_mgr_unlock(void)
+{
+    (void) os_mutex_release(&charge_control_mgr.mgr_lock);
+}
+
+static int
+charge_control_mgr_match_bydevname(struct charge_control *cc, void *arg)
+{
+    char *devname;
+
+    devname = (char *) arg;
+
+    if (!strcmp(cc->cc_dev->od_name, devname)) {
+        return (1);
+    }
+
+    return (0);
+}
+
+static void
+charge_control_mgr_remove(struct charge_control *cc)
+{
+    SLIST_REMOVE(&charge_control_mgr.mgr_charge_control_list, cc,
+            charge_control, cc_next);
+}
+
+static void
+charge_control_mgr_insert(struct charge_control *cc)
+{
+    struct charge_control *cursor, *prev;
+
+    prev = cursor = NULL;
+    if (!cc->cc_poll_rate) {
+        SLIST_FOREACH(cursor, &charge_control_mgr.mgr_charge_control_list, 
cc_next) {
+            prev = cursor;
+        }
+        goto insert;
+    }
+
+    prev = cursor = NULL;
+    SLIST_FOREACH(cursor, &charge_control_mgr.mgr_charge_control_list, 
cc_next) {
+        if (!cursor->cc_poll_rate) {
+            break;
+        }
+
+        if (OS_TIME_TICK_LT(cc->cc_next_run, cursor->cc_next_run)) {
+            break;
+        }
+
+        prev = cursor;
+    }
+
+insert:
+    if (prev == NULL) {
+        SLIST_INSERT_HEAD(&charge_control_mgr.mgr_charge_control_list, cc,
+                cc_next);
+    } else {
+        SLIST_INSERT_AFTER(prev, cc, cc_next);
+    }
+}
+
+static void
+charge_control_mgr_poll_bytype(struct charge_control *cc,
+        charge_control_type_t type, struct charge_control_type_traits *cctt,
+        os_time_t now)
+{
+    if (!cctt || !cctt->cctt_polls_left) {
+        /* Charge control read results. Every time a charge controller is read,
+         * all of its listeners are called by default. Specify NULL as a
+         * callback, because we just want to run all the listeners.
+         */
+
+        charge_control_read(cc, type, NULL, NULL, OS_TIMEOUT_NEVER);
+
+        charge_control_lock(cc);
+
+        if (cctt) {
+            if (!cctt->cctt_polls_left && cctt->cctt_poll_n) {
+                cctt->cctt_polls_left = cctt->cctt_poll_n;
+                cctt->cctt_polls_left--;
+            }
+        }
+
+        /* Unlock the sensor to allow other access */
+        charge_control_unlock(cc);
+
+    } else {
+        cctt->cctt_polls_left--;
+    }
+}
+
+static void
+charge_control_mgr_evq_set(struct os_eventq *evq)
+{
+    assert(evq != NULL);
+    charge_control_mgr.mgr_eventq = evq;
+}
+
+static void
+charge_control_mgr_init(void)
+{
+    struct os_timeval ostv;
+    struct os_timezone ostz;
+
+#ifdef MYNEWT_VAL_CHARGE_CONTROL_MGR_EVQ
+    charge_control_mgr_evq_set(MYNEWT_VAL(CHARGE_CONTROL_MGR_EVQ));
+#else
+    charge_control_mgr_evq_set(os_eventq_dflt_get());
+#endif
+
+    /**
+     * Initialize charge control polling callout and set it to fire on boot.
+     */
+    os_callout_init(&charge_control_mgr.mgr_wakeup_callout,
+            charge_control_mgr_evq_get(), charge_control_mgr_wakeup_event,
+            NULL);
+
+    /* Initialize sensor cputime update callout and set it to fire after an
+     * hour, CPU time gets wrapped in 4295 seconds,
+     * hence the hardcoded value of 3600 seconds, We make sure that the
+     * cputime never gets wrapped more than once.
+     */
+    os_gettimeofday(&ostv, &ostz);
+
+    charge_control_base_ts.cct_ostv = ostv;
+    charge_control_base_ts.cct_ostz = ostz;
+    charge_control_base_ts.cct_cputime = os_cputime_get32();
+
+    os_callout_init(&cct_up_osco, charge_control_mgr_evq_get(),
+            charge_control_base_ts_update_event, NULL);
+    os_callout_reset(&cct_up_osco, OS_TICKS_PER_SEC);
+
+    os_mutex_init(&charge_control_mgr.mgr_lock);
+}
+
+int
+charge_control_mgr_register(struct charge_control *cc)
+{
+    int rc;
+
+    rc = charge_control_mgr_lock();
+    if (rc != 0) {
+        goto err;
+    }
+
+    rc = charge_control_lock(cc);
+    if (rc != 0) {
+        goto err;
+    }
+
+    charge_control_mgr_insert(cc);
+
+    charge_control_unlock(cc);
+
+    charge_control_mgr_unlock();
+
+    return (0);
+err:
+    return (rc);
+}
+
+struct os_eventq *
+charge_control_mgr_evq_get(void)
+{
+    return (charge_control_mgr.mgr_eventq);
+}
+
+struct charge_control *
+charge_control_mgr_find_next(charge_control_mgr_compare_func_t compare_func,
+        void *arg, struct charge_control *prev_cursor)
+{
+    struct charge_control *cursor;
+    int rc;
+
+    cursor = NULL;
+
+    /* Couldn't acquire lock of charge control list, exit */
+    rc = charge_control_mgr_lock();
+    if (rc != 0) {
+        goto done;
+    }
+
+    cursor = prev_cursor;
+    if (cursor == NULL) {
+        cursor = SLIST_FIRST(&charge_control_mgr.mgr_charge_control_list);
+    } else {
+        cursor = SLIST_NEXT(prev_cursor, cc_next);
+    }
+
+    while (cursor != NULL) {
+        if (compare_func(cursor, arg)) {
+            break;
+        }
+        cursor = SLIST_NEXT(cursor, cc_next);
+    }
+
+    charge_control_mgr_unlock();
+
+done:
+    return (cursor);
+}
+
+struct charge_control *
+charge_control_mgr_find_next_bytype(charge_control_type_t type,
+        struct charge_control *prev_cursor)
+{
+    return (charge_control_mgr_find_next(charge_control_mgr_match_bytype,
+            (void *) &type, prev_cursor));
+}
+
+struct charge_control *
+charge_control_mgr_find_next_bydevname(char *devname,
+        struct charge_control *prev_cursor)
+{
+    return (charge_control_mgr_find_next(charge_control_mgr_match_bydevname,
+            devname, prev_cursor));
+}
+
+int
+charge_control_mgr_match_bytype(struct charge_control *cc, void *arg)
+{
+    charge_control_type_t *type;
+
+    type = (charge_control_type_t *) arg;
+
+    /* cc_types is a bitmask that contains the supported charge control types
+     * for this charge controller, and type is the bitmask we're searching for
+     * We also look at the mask as the driver might be configured to work in a
+     * mode where only some of the charge control types are supported but not
+     * all. Compare the three, and if there is a match, return 1. If it is not
+     * supported, return 0.
+     */
+    return (*type & cc->cc_types & cc->cc_mask) ? 1 : 0;
+}
+
+int
+charge_control_set_poll_rate_ms(char *devname, uint32_t poll_rate)
+{
+    struct charge_control *charge_control;
+    os_time_t next_wakeup;
+    os_time_t now;
+    int rc;
+
+    os_callout_stop(&charge_control_mgr.mgr_wakeup_callout);
+
+    charge_control = charge_control_mgr_find_next_bydevname(devname, NULL);
+    if (!charge_control) {
+        rc = SYS_EINVAL;
+        goto err;
+    }
+
+    charge_control_lock(charge_control);
+
+    now = os_time_get();
+
+    os_time_ms_to_ticks(poll_rate, &next_wakeup);
+
+    charge_control_update_poll_rate(charge_control, poll_rate);
+
+    charge_control_update_nextrun(charge_control, now);
+
+    charge_control_unlock(charge_control);
+
+    charge_control = charge_control_find_min_nextrun(now, &next_wakeup);
+
+    os_callout_reset(&charge_control_mgr.mgr_wakeup_callout, next_wakeup);
+
+    return 0;
+err:
+    return rc;
+}
+
+int
+charge_control_set_n_poll_rate(char * devname,
+        struct charge_control_type_traits *cctt)
+{
+    struct charge_control *cc;
+    struct charge_control_type_traits *cctt_tmp;
+    int rc;
+
+    if (!cctt) {
+        rc = SYS_EINVAL;
+        goto err;
+    }
+
+    cctt_tmp = NULL;
+
+    cc = charge_control_get_type_traits_byname(devname, &cctt_tmp,
+                                           cctt->cctt_charge_control_type);
+    if (!cc) {
+        rc = SYS_EINVAL;
+        goto err;
+    }
+
+    if (!cctt_tmp && cctt) {
+        rc = charge_control_insert_type_trait(cc, cctt);
+        rc |= charge_control_lock(cc);
+        if (rc) {
+            goto err;
+        }
+        cctt_tmp = cctt;
+        cctt_tmp->cctt_polls_left = cctt->cctt_poll_n;
+        charge_control_unlock(cc);
+    } else if (cctt_tmp) {
+        rc = charge_control_remove_type_trait(cc, cctt_tmp);
+        if (rc) {
+            goto err;
+        }
+
+        charge_control_lock(cc);
+
+        cctt_tmp->cctt_poll_n = cctt->cctt_poll_n;
+        cctt_tmp->cctt_polls_left = cctt->cctt_poll_n;
+
+        charge_control_unlock(cc);
+
+        rc = charge_control_insert_type_trait(cc, cctt_tmp);
+        if (rc) {
+            goto err;
+        }
+
+    } else {
+        rc = SYS_EINVAL;
+        goto err;
+    }
+
+    return 0;
+err:
+    return rc;
+}
+
+void
+charge_control_mgr_put_read_evt(void *arg)
+{
+    charge_control_read_event.ev_arg = arg;
+    os_eventq_put(charge_control_mgr_evq_get(), &charge_control_read_event);
+}
diff --git a/hw/charge-control/src/charge_control_priv.h 
b/hw/charge-control/src/charge_control_priv.h
new file mode 100644
index 000000000..884310601
--- /dev/null
+++ b/hw/charge-control/src/charge_control_priv.h
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+struct charge_control_read_ev_ctx {
+    /* The sensor for which the ev cb should be called */
+    struct charge_control *ccrec_charge_control;
+    /* The sensor type */
+    charge_control_type_t ccrec_type;
+};
diff --git a/hw/charge-control/syscfg.yml b/hw/charge-control/syscfg.yml
new file mode 100644
index 000000000..efb52332a
--- /dev/null
+++ b/hw/charge-control/syscfg.yml
@@ -0,0 +1,29 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Package: hw/charge-control
+
+syscfg.defs:
+    CHARGE_CONTROL_CLI:
+        description: 'Whether or not to enable the charge control shell 
support'
+        value: 1
+
+    CHARGE_CONTROL_MGR_EVQ:
+        description: >
+            Specify the eventq to be used by the charge control manager
+        value:
diff --git a/hw/drivers/chg_ctrl/bq24040/include/bq24040/bq24040.h 
b/hw/drivers/chg_ctrl/bq24040/include/bq24040/bq24040.h
new file mode 100644
index 000000000..1f0e5b7da
--- /dev/null
+++ b/hw/drivers/chg_ctrl/bq24040/include/bq24040/bq24040.h
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * resarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef __BQ24040_H__
+#define __BQ24040_H__
+
+#include "os/os.h"
+#include "hal/hal_gpio.h"
+#include "charge-control/charge_control.h"
+
+/**
+ * Configuration setting for the temperature sense (TS) pin.  If temperature
+ * sensing is enabled in the hardware, the BQ24040 will always be considered to
+ * be "enabled".
+ */
+typedef enum {
+       /** Temperature sensing is not enabled in the hardware */
+       BQ24040_TS_MODE_DISABLED = 0,
+       /** Temperature sensing is enabled in the hardware */
+       BQ24040_TS_MODE_ENABLED,
+} bq24040_ts_mode_t;
+
+typedef void (*bq24040_interrupt_handler)(void *arg);
+
+struct bq24040_pin {
+       int bp_pin_num;
+       enum hal_gpio_mode_e bp_pin_direction;
+       int bp_init_value;
+       hal_gpio_irq_trig_t bp_irq_trig;
+       hal_gpio_pull_t bp_pull;
+       bq24040_interrupt_handler bp_irq_fn;
+};
+
+struct bq24040_cfg {
+       struct bq24040_pin *bc_pg_pin;
+       struct bq24040_pin *bc_chg_pin;
+       struct bq24040_pin *bc_ts_pin;
+       struct bq24040_pin *bc_iset2_pin;
+       bq24040_ts_mode_t       bc_ts_mode;
+       charge_control_type_t   bc_mask;
+};
+
+struct bq24040 {
+       struct os_dev   b_dev;
+       struct charge_control   b_chg_ctrl;
+       struct bq24040_cfg b_cfg;
+       os_time_t       b_last_read_time;
+       bool b_is_enabled;
+};
+
+/**
+ * Initialize the bq24040
+ *
+ * @param dev  Pointer to the bq24040_dev device descriptor
+ *
+ * @return 0 on success, and non-zero error code on failure
+ */
+int
+bq24040_init(struct os_dev *dev, void *arg);
+
+/**
+ * Configure BQ24040 sensor
+ *
+ * @param Sensor device BQ24040 structure
+ * @param Sensor device BQ24040 configuration structure
+ *
+ * @return 0 on success, non-zero on failure
+ */
+int
+bq24040_config(struct bq24040 *, struct bq24040_cfg *);
+
+/**
+ * Reads the state of the charge source
+ *
+ * @param Sensor device BQ24040 structure
+ * @param Charge source status to be returned by the function (0 if charge
+ *     source is not detected, 1 if it is detected)
+ *
+ * @return 0 on success, non-zero on failure
+ */
+int
+bq24040_get_charge_source_status(struct bq24040 *, int *);
+
+/**
+ * Reads the battery charging status
+ *
+ * @param Sensor device BQ24040 structure
+ * @param Battery charging status returned by the function (0 if the battery is
+ *     not charging, 1 if it is charging)
+ *
+ * @return 0 on success, non-zero on failure
+ */
+int
+bq24040_get_charging_status(struct bq24040 *, int *);
+
+/**
+ * Set the BQ24040 TS pin to logic 1 (if configured), enabling charger mode on
+ * the IC
+ *
+ * @param Sensor device BQ24040 structure
+ *
+ * @return 0 on success, and non-zero error code on failure
+ */
+int
+bq24040_enable(struct bq24040 *);
+
+/**
+ * Clear the BQ24040 TS pin to logic 0 (if configured), disabling the IC
+ *
+ * @param Sensor device BQ24040 structure
+ *
+ * @return 0 on success, and non-zero error code on failure
+ */
+int
+bq24040_disable(struct bq24040 *);
+
+#endif
diff --git a/hw/drivers/chg_ctrl/bq24040/pkg.yml 
b/hw/drivers/chg_ctrl/bq24040/pkg.yml
new file mode 100644
index 000000000..26aa7e7f4
--- /dev/null
+++ b/hw/drivers/chg_ctrl/bq24040/pkg.yml
@@ -0,0 +1,29 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+pkg.name: hw/drivers/chg_ctrl/bq24040
+pkg.description: 
+pkg.keywords:
+    - Charge Controller
+    - bq24040
+
+pkg.deps:
+    - "@apache-mynewt-core/kernel/os"
+    - "@apache-mynewt-core/hw/hal"
+    - "@apache-mynewt-core/hw/charge-control"
diff --git a/hw/drivers/chg_ctrl/bq24040/src/bq24040.c 
b/hw/drivers/chg_ctrl/bq24040/src/bq24040.c
new file mode 100644
index 000000000..c8eaa9e20
--- /dev/null
+++ b/hw/drivers/chg_ctrl/bq24040/src/bq24040.c
@@ -0,0 +1,343 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * resarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include "defs/error.h"
+#include "hal/hal_gpio.h"
+#include "bq24040/bq24040.h"
+#include "console/console.h"
+#include "log/log.h"
+
+/* Exports for the charge control API */
+static int bq24040_chg_ctrl_read(struct charge_control *, 
charge_control_type_t,
+        charge_control_data_func_t, void *, uint32_t);
+static int bq24040_chg_ctrl_get_config(struct charge_control *,
+        charge_control_type_t, struct charge_control_cfg *);
+static int bq24040_chg_ctrl_set_config(struct charge_control *, void *);
+static int bq24040_chg_ctrl_get_status(struct charge_control *, int *);
+static int bq24040_chg_ctrl_enable(struct charge_control *);
+static int bq24040_chg_ctrl_disable(struct charge_control *);
+
+static const struct charge_control_driver g_bq24040_chg_ctrl_driver = {
+    .ccd_read = bq24040_chg_ctrl_read,
+    .ccd_get_config = bq24040_chg_ctrl_get_config,
+    .ccd_set_config = bq24040_chg_ctrl_set_config,
+    .ccd_get_status = bq24040_chg_ctrl_get_status,
+    .ccd_get_fault = NULL,
+    .ccd_enable = bq24040_chg_ctrl_enable,
+    .ccd_disable = bq24040_chg_ctrl_disable,
+};
+
+static int
+bq24040_configure_pin(struct bq24040_pin *pin)
+{
+    if ((!pin) || (pin->bp_pin_num == -1)) {
+        return 0;
+    }
+
+    if (pin->bp_pin_direction == HAL_GPIO_MODE_IN) {
+        if (pin->bp_irq_trig != HAL_GPIO_TRIG_NONE) {
+            assert(pin->bp_irq_fn != NULL);
+            return hal_gpio_irq_init(pin->bp_pin_num, pin->bp_irq_fn,
+                    NULL, pin->bp_irq_trig, pin->bp_pull);
+        } else {
+            return hal_gpio_init_in(pin->bp_pin_num, pin->bp_pull);
+        }
+    } else if (pin->bp_pin_direction == HAL_GPIO_MODE_OUT) {
+        return hal_gpio_init_out(pin->bp_pin_num, pin->bp_init_value);
+    }
+
+    return SYS_EINVAL;
+}
+
+int
+bq24040_init(struct os_dev *dev, void *arg)
+{
+    struct bq24040 *bq24040;
+    struct charge_control *cc;
+    int rc;
+
+    if (!dev) {
+        rc = SYS_ENODEV;
+        goto err;
+    }
+
+    bq24040 = (struct bq24040 *)dev;
+
+    cc = &bq24040->b_chg_ctrl;
+
+    rc = charge_control_init(cc, dev);
+    if (rc) {
+        goto err;
+    }
+
+    /* Add the driver with all the supported types */
+    rc = charge_control_set_driver(cc, CHARGE_CONTROL_TYPE_STATUS,
+            (struct charge_control_driver *)&g_bq24040_chg_ctrl_driver);
+    if (rc) {
+        goto err;
+    }
+
+    rc = charge_control_mgr_register(cc);
+    if (rc) {
+        goto err;
+    }
+
+    bq24040->b_is_enabled = false;
+
+    return 0;
+err:
+    return rc;
+}
+
+int
+bq24040_config(struct bq24040 *bq24040, struct bq24040_cfg *cfg)
+{
+    int rc;
+
+    rc = charge_control_set_type_mask(&(bq24040->b_chg_ctrl), cfg->bc_mask);
+    if (rc) {
+        goto err;
+    }
+
+    bq24040->b_cfg = *cfg;
+
+    rc = bq24040_configure_pin(cfg->bc_pg_pin);
+    if (rc) {
+        goto err;
+    }
+
+    rc = bq24040_configure_pin(cfg->bc_chg_pin);
+    if (rc) {
+        goto err;
+    }
+
+    if (cfg->bc_ts_mode == BQ24040_TS_MODE_DISABLED) {
+        rc = bq24040_configure_pin(cfg->bc_ts_pin);
+        if (rc) {
+            goto err;
+        }
+    } else {
+        bq24040->b_is_enabled = true;
+    }
+
+    rc = bq24040_configure_pin(cfg->bc_iset2_pin);
+    if (rc) {
+        goto err;
+    }
+
+    return 0;
+err:
+    return (rc);
+}
+
+int
+bq24040_get_charge_source_status(struct bq24040 *bq24040, int *status)
+{
+    int read_val;
+
+    if (bq24040->b_cfg.bc_pg_pin->bp_pin_num != -1) {
+        read_val = hal_gpio_read(bq24040->b_cfg.bc_pg_pin->bp_pin_num);
+        *status = read_val == 0 ? 1 : 0;
+        return 0;
+    }
+    return 1;
+}
+
+int
+bq24040_get_charging_status(struct bq24040 *bq24040, int *status)
+{
+    int read_val;
+
+    if (bq24040->b_cfg.bc_chg_pin->bp_pin_num != -1) {
+        read_val = hal_gpio_read(bq24040->b_cfg.bc_chg_pin->bp_pin_num);
+        *status = read_val == 0 ? 1 : 0;
+        return 0;
+    }
+    return 1;
+}
+
+int
+bq24040_enable(struct bq24040 *bq24040)
+{
+    /* Don't enable if already enabled */
+    if (bq24040->b_is_enabled) {
+        return 0;
+    }
+
+    if (bq24040->b_cfg.bc_ts_pin->bp_pin_num != -1) {
+        hal_gpio_write(bq24040->b_cfg.bc_ts_pin->bp_pin_num, 1);
+        bq24040->b_is_enabled = true;
+        return 0;
+    }
+    return SYS_EINVAL;
+}
+
+int
+bq24040_disable(struct bq24040 *bq24040)
+{
+    /* Don't disable if already disabled */
+    if (!bq24040->b_is_enabled) {
+        return 0;
+    }
+
+    if (bq24040->b_cfg.bc_ts_pin->bp_pin_num != -1) {
+        hal_gpio_write(bq24040->b_cfg.bc_ts_pin->bp_pin_num, 0);
+        bq24040->b_is_enabled = false;
+        return 0;
+    }
+    return SYS_EINVAL;
+}
+
+/**
+ * Reads the charge source and charging status pins (if configured)
+ *
+ * @param The bq24040 charge controller to read from
+ * @param The type(s) of sensor values to read.  Mask containing that type, 
provide
+ *        all, to get all values.
+ * @param The function to call with each value read.  If NULL, it calls all
+ *        sensor listeners associated with this function.
+ * @param The argument to pass to the read callback.
+ * @param Timeout.  If block until result, specify OS_TIMEOUT_NEVER, 0 returns
+ *        immediately (no wait.)
+ *
+ * @return 0 on success, non-zero error code on failure.
+ */
+static int
+bq24040_chg_ctrl_read(struct charge_control *cc, charge_control_type_t type,
+        charge_control_data_func_t data_func, void *data_arg, uint32_t timeout)
+{
+    int status;
+    int rc;
+
+    if (!(type & CHARGE_CONTROL_TYPE_STATUS)) {
+        rc = SYS_EINVAL;
+        goto err;
+    }
+
+    status = CHARGE_CONTROL_STATUS_DISABLED;
+
+    rc = bq24040_chg_ctrl_get_status(cc, &status);
+    if (rc) {
+        goto err;
+    }
+
+    data_func(cc, data_arg, (void*)&status, CHARGE_CONTROL_TYPE_STATUS);
+
+    return 0;
+err:
+    return rc;
+}
+
+/**
+ * Get the configuration of the bq24040.
+ *
+ * @param The type of sensor value to get configuration for
+ * @param A pointer to the config structure to place the returned result into.
+ *
+ * @return 0 on success, non-zero error code on failure.
+ */
+static int
+bq24040_chg_ctrl_get_config(struct charge_control *cc, 
+        charge_control_type_t type, struct charge_control_cfg * cfg)
+{
+    int rc;
+
+    if (!(type & CHARGE_CONTROL_TYPE_STATUS)) {
+        rc = SYS_EINVAL;
+        goto err;
+    }
+
+    return 0;
+err:
+    return rc;
+}
+
+static int
+bq24040_chg_ctrl_set_config(struct charge_control *cc, void *cfg)
+{
+    struct bq24040* bq24040 = (struct bq24040 *)CHARGE_CONTROL_GET_DEVICE(cc);
+
+    return bq24040_config(bq24040, (struct bq24040_cfg*)cfg);
+}
+
+static int
+bq24040_chg_ctrl_get_status(struct charge_control *cc, int * status)
+{
+    struct bq24040 * bq24040;
+    int ch_src_present, is_charging;
+    int rc;
+
+    bq24040 = (struct bq24040 *)CHARGE_CONTROL_GET_DEVICE(cc);
+    if (bq24040 == NULL) {
+        return SYS_ENODEV;
+    }
+
+    if (bq24040->b_is_enabled) {
+        rc = bq24040_get_charge_source_status(bq24040, &ch_src_present);
+        if (rc) {
+            return rc;
+        }
+
+        rc = bq24040_get_charging_status(bq24040, &is_charging);
+        if (rc) {
+            return rc;
+        }
+
+        if (ch_src_present) {
+            if (is_charging) {
+                *status = CHARGE_CONTROL_STATUS_CHARGING;
+            } else {
+                *status = CHARGE_CONTROL_STATUS_CHARGE_COMPLETE;
+            }
+        } else {
+            *status = CHARGE_CONTROL_STATUS_NO_SOURCE;
+        }
+    } else {
+        *status = CHARGE_CONTROL_STATUS_DISABLED;
+    }
+
+    return 0;
+}
+
+static int
+bq24040_chg_ctrl_enable(struct charge_control *cc)
+{
+    struct bq24040 *bq24040 = (struct bq24040 *)CHARGE_CONTROL_GET_DEVICE(cc);
+
+    if (bq24040 == NULL) {
+        return SYS_ENODEV;
+    }
+
+    return bq24040_enable(bq24040);
+}
+
+static int
+bq24040_chg_ctrl_disable(struct charge_control *cc)
+{
+    struct bq24040 *bq24040 = (struct bq24040 *)CHARGE_CONTROL_GET_DEVICE(cc);
+
+    if (bq24040 == NULL) {
+        return SYS_ENODEV;
+    }
+
+    return bq24040_disable(bq24040);
+}


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to