This is an automated email from the ASF dual-hosted git repository.

vipulrahane pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git


The following commit(s) were added to refs/heads/master by this push:
     new 4f36d51  charge-control: Adding sensor-like interface for battery 
charge controller ICs (#814)
4f36d51 is described below

commit 4f36d51568348afdd2572a4b3e7e80212f16de73
Author: brianpax <br...@juul.com>
AuthorDate: Mon Feb 19 10:22:14 2018 -0800

    charge-control: Adding sensor-like interface for battery charge controller 
ICs (#814)
    
    - Adding charge-control interface
    - Adding bq24040 driver
---
 .../include/charge-control/charge_control.h        |  666 +++++++++++++
 hw/charge-control/pkg.yml                          |   36 +
 hw/charge-control/src/charge_control.c             | 1039 ++++++++++++++++++++
 hw/charge-control/src/charge_control_priv.h        |   25 +
 hw/charge-control/syscfg.yml                       |   29 +
 .../chg_ctrl/bq24040/include/bq24040/bq24040.h     |  133 +++
 hw/drivers/chg_ctrl/bq24040/pkg.yml                |   29 +
 hw/drivers/chg_ctrl/bq24040/src/bq24040.c          |  343 +++++++
 8 files changed, 2300 insertions(+)

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 0000000..9a8315c
--- /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 0000000..355de70
--- /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 0000000..4b153e8
--- /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 0000000..8843106
--- /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 0000000..efb5233
--- /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 0000000..1f0e5b7
--- /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 0000000..26aa7e7
--- /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 0000000..c8eaa9e
--- /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);
+}

-- 
To stop receiving notification emails like this one, please contact
vipulrah...@apache.org.

Reply via email to