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.