stats: i2c: I2C stats
Adds the ability for I2C drivers to report synchronous transfer stats using a I2C specific macro to define the device instance. The macro creates a container for device_state which allows for per instance device class common data structure to be used in the device class api (ex: i2c.h). This is used to maintain per driver instance stats for all i2c drivers. This is a reusable idea across other device classes as desired. Using Kconfig device class stats may be turned on/off individually this way as well, in this case I2C_STATS. Signed-off-by: Tom Burdick <thomas.burdick@intel.com>
This commit is contained in:
parent
e29d9e1e2f
commit
7b1349cfe6
37 changed files with 253 additions and 66 deletions
|
@ -92,6 +92,31 @@ typedef int16_t device_handle_t;
|
|||
DEVICE_DEFINE(Z_SYS_NAME(init_fn), drv_name, init_fn, NULL, \
|
||||
NULL, NULL, level, prio, NULL)
|
||||
|
||||
/* Node paths can exceed the maximum size supported by device_get_binding() in user mode,
|
||||
* so synthesize a unique dev_name from the devicetree node.
|
||||
*
|
||||
* The ordinal used in this name can be mapped to the path by
|
||||
* examining zephyr/include/generated/device_extern.h header. If the
|
||||
* format of this conversion changes, gen_defines should be updated to
|
||||
* match it.
|
||||
*/
|
||||
#define Z_DEVICE_DT_DEV_NAME(node_id) _CONCAT(dts_ord_, DT_DEP_ORD(node_id))
|
||||
|
||||
/* Synthesize a unique name for the device state associated with
|
||||
* dev_name.
|
||||
*/
|
||||
#define Z_DEVICE_STATE_NAME(dev_name) _CONCAT(__devstate_, dev_name)
|
||||
|
||||
/**
|
||||
* @brief Utility macro to define and initialize the device state.
|
||||
*
|
||||
* @param node_id Devicetree node id of the device.
|
||||
* @param dev_name Device name.
|
||||
*/
|
||||
#define Z_DEVICE_STATE_DEFINE(node_id, dev_name) \
|
||||
static struct device_state Z_DEVICE_STATE_NAME(dev_name) \
|
||||
__attribute__((__section__(".z_devstate")));
|
||||
|
||||
/**
|
||||
* @def DEVICE_DEFINE
|
||||
*
|
||||
|
@ -129,9 +154,11 @@ typedef int16_t device_handle_t;
|
|||
*/
|
||||
#define DEVICE_DEFINE(dev_name, drv_name, init_fn, pm_device, \
|
||||
data_ptr, cfg_ptr, level, prio, api_ptr) \
|
||||
Z_DEVICE_STATE_DEFINE(DT_INVALID_NODE, dev_name) \
|
||||
Z_DEVICE_DEFINE(DT_INVALID_NODE, dev_name, drv_name, init_fn, \
|
||||
pm_device, \
|
||||
data_ptr, cfg_ptr, level, prio, api_ptr)
|
||||
data_ptr, cfg_ptr, level, prio, api_ptr, \
|
||||
&Z_DEVICE_STATE_NAME(dev_name))
|
||||
|
||||
/**
|
||||
* @def DEVICE_DT_NAME
|
||||
|
@ -187,11 +214,14 @@ typedef int16_t device_handle_t;
|
|||
#define DEVICE_DT_DEFINE(node_id, init_fn, pm_device, \
|
||||
data_ptr, cfg_ptr, level, prio, \
|
||||
api_ptr, ...) \
|
||||
Z_DEVICE_STATE_DEFINE(node_id, Z_DEVICE_DT_DEV_NAME(node_id)) \
|
||||
Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_NAME(node_id), \
|
||||
DEVICE_DT_NAME(node_id), init_fn, \
|
||||
pm_device, \
|
||||
data_ptr, cfg_ptr, level, prio, \
|
||||
api_ptr, __VA_ARGS__)
|
||||
api_ptr, \
|
||||
&Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_NAME(node_id)), \
|
||||
__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @def DEVICE_DT_INST_DEFINE
|
||||
|
@ -693,21 +723,6 @@ static inline bool device_is_ready(const struct device *dev)
|
|||
* @}
|
||||
*/
|
||||
|
||||
/* Node paths can exceed the maximum size supported by device_get_binding() in user mode,
|
||||
* so synthesize a unique dev_name from the devicetree node.
|
||||
*
|
||||
* The ordinal used in this name can be mapped to the path by
|
||||
* examining zephyr/include/generated/device_extern.h header. If the
|
||||
* format of this conversion changes, gen_defines should be updated to
|
||||
* match it.
|
||||
*/
|
||||
#define Z_DEVICE_DT_DEV_NAME(node_id) _CONCAT(dts_ord_, DT_DEP_ORD(node_id))
|
||||
|
||||
/* Synthesize a unique name for the device state associated with
|
||||
* dev_name.
|
||||
*/
|
||||
#define Z_DEVICE_STATE_NAME(dev_name) _CONCAT(__devstate_, dev_name)
|
||||
|
||||
/** Synthesize the name of the object that holds device ordinal and
|
||||
* dependency data. If the object doesn't come from a devicetree
|
||||
* node, use dev_name.
|
||||
|
@ -735,8 +750,7 @@ static inline bool device_is_ready(const struct device *dev)
|
|||
* include power management and dependency handles.
|
||||
*/
|
||||
#define Z_DEVICE_DEFINE_PRE(node_id, dev_name, ...) \
|
||||
Z_DEVICE_DEFINE_HANDLES(node_id, dev_name, __VA_ARGS__) \
|
||||
Z_DEVICE_STATE_DEFINE(node_id, dev_name)
|
||||
Z_DEVICE_DEFINE_HANDLES(node_id, dev_name, __VA_ARGS__)
|
||||
|
||||
/* Initial build provides a record that associates the device object
|
||||
* with its devicetree ordinal, and provides the dependency ordinals.
|
||||
|
@ -805,7 +819,7 @@ BUILD_ASSERT(sizeof(device_handle_t) == 2, "fix the linker scripts");
|
|||
* dependency handles that come from outside devicetree.
|
||||
*/
|
||||
#define Z_DEVICE_DEFINE(node_id, dev_name, drv_name, init_fn, pm_device,\
|
||||
data_ptr, cfg_ptr, level, prio, api_ptr, ...) \
|
||||
data_ptr, cfg_ptr, level, prio, api_ptr, state_ptr, ...) \
|
||||
Z_DEVICE_DEFINE_PRE(node_id, dev_name, __VA_ARGS__) \
|
||||
COND_CODE_1(DT_NODE_EXISTS(node_id), (), (static)) \
|
||||
const Z_DECL_ALIGN(struct device) \
|
||||
|
@ -814,7 +828,7 @@ BUILD_ASSERT(sizeof(device_handle_t) == 2, "fix the linker scripts");
|
|||
.name = drv_name, \
|
||||
.config = (cfg_ptr), \
|
||||
.api = (api_ptr), \
|
||||
.state = &Z_DEVICE_STATE_NAME(dev_name), \
|
||||
.state = (state_ptr), \
|
||||
.data = (data_ptr), \
|
||||
COND_CODE_1(CONFIG_PM_DEVICE, (.pm = pm_device,), ()) \
|
||||
Z_DEVICE_DEFINE_INIT(node_id, dev_name) \
|
||||
|
|
|
@ -337,6 +337,163 @@ struct i2c_slave_config {
|
|||
const struct i2c_slave_callbacks *callbacks;
|
||||
};
|
||||
|
||||
#if defined(CONFIG_I2C_STATS) || defined(__DOXYGEN__)
|
||||
|
||||
#include <stats/stats.h>
|
||||
|
||||
/** @cond INTERNAL_HIDDEN */
|
||||
|
||||
STATS_SECT_START(i2c)
|
||||
STATS_SECT_ENTRY32(bytes_read)
|
||||
STATS_SECT_ENTRY32(bytes_written)
|
||||
STATS_SECT_ENTRY32(message_count)
|
||||
STATS_SECT_ENTRY32(transfer_call_count)
|
||||
STATS_SECT_END;
|
||||
|
||||
STATS_NAME_START(i2c)
|
||||
STATS_NAME(i2c, bytes_read)
|
||||
STATS_NAME(i2c, bytes_written)
|
||||
STATS_NAME(i2c, message_count)
|
||||
STATS_NAME(i2c, transfer_call_count)
|
||||
STATS_NAME_END(i2c);
|
||||
|
||||
/** @endcond */
|
||||
|
||||
|
||||
/**
|
||||
* @brief I2C specific device state which allows for i2c device class specific additions
|
||||
*/
|
||||
struct i2c_device_state {
|
||||
struct device_state devstate;
|
||||
struct stats_i2c stats;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Updates the i2c stats for i2c transfers
|
||||
*
|
||||
* @param dev I2C device to update stats for
|
||||
* @param msgs Array of struct i2c_msg
|
||||
* @param num_msgs Number of i2c_msgs
|
||||
*/
|
||||
static inline void i2c_xfer_stats(const struct device *dev, struct i2c_msg *msgs,
|
||||
uint8_t num_msgs)
|
||||
{
|
||||
struct i2c_device_state *state =
|
||||
CONTAINER_OF(dev->state, struct i2c_device_state, devstate);
|
||||
uint32_t bytes_read = 0U;
|
||||
uint32_t bytes_written = 0U;
|
||||
|
||||
STATS_INC(state->stats, transfer_call_count);
|
||||
STATS_INCN(state->stats, message_count, num_msgs);
|
||||
for (uint8_t i = 0U; i < num_msgs; i++) {
|
||||
if (msgs[i].flags & I2C_MSG_READ) {
|
||||
bytes_read += msgs[i].len;
|
||||
}
|
||||
if (msgs[i].flags & I2C_MSG_WRITE) {
|
||||
bytes_written += msgs[i].len;
|
||||
}
|
||||
}
|
||||
STATS_INCN(state->stats, bytes_read, bytes_read);
|
||||
STATS_INCN(state->stats, bytes_written, bytes_written);
|
||||
}
|
||||
|
||||
/** @cond INTERNAL_HIDDEN */
|
||||
|
||||
/**
|
||||
* @brief Define a statically allocated and section assigned i2c device state
|
||||
*/
|
||||
#define Z_I2C_DEVICE_STATE_DEFINE(node_id, dev_name) \
|
||||
static struct i2c_device_state Z_DEVICE_STATE_NAME(dev_name) \
|
||||
__attribute__((__section__(".z_devstate")));
|
||||
|
||||
/**
|
||||
* @brief Define an i2c device init wrapper function
|
||||
*
|
||||
* This does device instance specific initialization of common data (such as stats)
|
||||
* and calls the given init_fn
|
||||
*/
|
||||
#define Z_I2C_INIT_FN(dev_name, init_fn) \
|
||||
static inline int UTIL_CAT(dev_name, _init)(const struct device *dev) \
|
||||
{ \
|
||||
struct i2c_device_state *state = \
|
||||
CONTAINER_OF(dev->state, struct i2c_device_state, devstate); \
|
||||
stats_init(&state->stats.s_hdr, STATS_SIZE_32, 4, \
|
||||
STATS_NAME_INIT_PARMS(i2c)); \
|
||||
stats_register(dev->name, &(state->stats.s_hdr)); \
|
||||
return init_fn(dev); \
|
||||
}
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/**
|
||||
* @brief Like DEVICE_DT_DEFINE() with I2C specifics.
|
||||
*
|
||||
* @details Defines a device which implements the I2C API. May
|
||||
* generate a custom device_state container struct and init_fn
|
||||
* wrapper when needed depending on I2C @kconfig{CONFIG_I2C_STATS}.
|
||||
*
|
||||
* @param node_id The devicetree node identifier.
|
||||
*
|
||||
* @param init_fn Name of the init function of the driver.
|
||||
*
|
||||
* @param pm_device PM device resources reference (NULL if device does not use PM).
|
||||
*
|
||||
* @param data_ptr Pointer to the device's private data.
|
||||
*
|
||||
* @param cfg_ptr The address to the structure containing the
|
||||
* configuration information for this instance of the driver.
|
||||
*
|
||||
* @param level The initialization level. See SYS_INIT() for
|
||||
* details.
|
||||
*
|
||||
* @param prio Priority within the selected initialization level. See
|
||||
* SYS_INIT() for details.
|
||||
*
|
||||
* @param api_ptr Provides an initial pointer to the API function struct
|
||||
* used by the driver. Can be NULL.
|
||||
*/
|
||||
#define I2C_DEVICE_DT_DEFINE(node_id, init_fn, pm_device, \
|
||||
data_ptr, cfg_ptr, level, prio, \
|
||||
api_ptr, ...) \
|
||||
Z_I2C_DEVICE_STATE_DEFINE(node_id, Z_DEVICE_DT_DEV_NAME(node_id)); \
|
||||
Z_I2C_INIT_FN(Z_DEVICE_DT_DEV_NAME(node_id), init_fn) \
|
||||
Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_NAME(node_id), \
|
||||
DEVICE_DT_NAME(node_id), \
|
||||
&UTIL_CAT(Z_DEVICE_DT_DEV_NAME(node_id), _init), \
|
||||
pm_device, \
|
||||
data_ptr, cfg_ptr, level, prio, \
|
||||
api_ptr, \
|
||||
&(Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_NAME(node_id)).devstate), \
|
||||
__VA_ARGS__)
|
||||
|
||||
#else /* CONFIG_I2C_STATS */
|
||||
|
||||
static inline void i2c_xfer_stats(const struct device *dev, struct i2c_msg *msgs,
|
||||
uint8_t num_msgs)
|
||||
{
|
||||
}
|
||||
|
||||
#define I2C_DEVICE_DT_DEFINE(node_id, init_fn, pm_device, \
|
||||
data_ptr, cfg_ptr, level, prio, \
|
||||
api_ptr, ...) \
|
||||
DEVICE_DT_DEFINE(node_id, &init_fn, pm_device, \
|
||||
data_ptr, cfg_ptr, level, prio, \
|
||||
api_ptr, __VA_ARGS__)
|
||||
|
||||
#endif /* CONFIG_I2C_STATS */
|
||||
|
||||
/**
|
||||
* @brief Like I2C_DEVICE_DT_DEFINE() for an instance of a DT_DRV_COMPAT compatible
|
||||
*
|
||||
* @param inst instance number. This is replaced by
|
||||
* <tt>DT_DRV_COMPAT(inst)</tt> in the call to I2C_DEVICE_DT_DEFINE().
|
||||
*
|
||||
* @param ... other parameters as expected by I2C_DEVICE_DT_DEFINE().
|
||||
*/
|
||||
#define I2C_DEVICE_DT_INST_DEFINE(inst, ...) \
|
||||
I2C_DEVICE_DT_DEFINE(DT_DRV_INST(inst), __VA_ARGS__)
|
||||
|
||||
|
||||
/**
|
||||
* @brief Configure operation of a host controller.
|
||||
*
|
||||
|
@ -429,7 +586,11 @@ static inline int z_impl_i2c_transfer(const struct device *dev,
|
|||
const struct i2c_driver_api *api =
|
||||
(const struct i2c_driver_api *)dev->api;
|
||||
|
||||
return api->transfer(dev, msgs, num_msgs, addr);
|
||||
int res = api->transfer(dev, msgs, num_msgs, addr);
|
||||
|
||||
i2c_xfer_stats(dev, msgs, num_msgs);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -828,9 +828,10 @@ static inline bool net_eth_get_vlan_status(struct net_if *iface)
|
|||
#if defined(CONFIG_NET_VLAN)
|
||||
#define Z_ETH_NET_DEVICE_INIT(node_id, dev_name, drv_name, init_fn, \
|
||||
pm_action_cb, data, cfg, prio, api, mtu) \
|
||||
Z_DEVICE_STATE_DEFINE(node_id, dev_name) \
|
||||
Z_DEVICE_DEFINE(node_id, dev_name, drv_name, init_fn, \
|
||||
pm_action_cb, data, cfg, POST_KERNEL, \
|
||||
prio, api); \
|
||||
prio, api, &Z_DEVICE_STATE_NAME(dev_name)); \
|
||||
NET_L2_DATA_INIT(dev_name, 0, NET_L2_GET_CTX_TYPE(ETHERNET_L2));\
|
||||
NET_IF_INIT(dev_name, 0, ETHERNET_L2, mtu, NET_VLAN_MAX_COUNT)
|
||||
|
||||
|
|
|
@ -2250,9 +2250,11 @@ struct net_if_api {
|
|||
#define Z_NET_DEVICE_INIT(node_id, dev_name, drv_name, init_fn, \
|
||||
pm_action_cb, data, cfg, prio, api, l2, \
|
||||
l2_ctx_type, mtu) \
|
||||
Z_DEVICE_STATE_DEFINE(node_id, dev_name) \
|
||||
Z_DEVICE_DEFINE(node_id, dev_name, drv_name, init_fn, \
|
||||
pm_action_cb, data, \
|
||||
cfg, POST_KERNEL, prio, api); \
|
||||
cfg, POST_KERNEL, prio, api, \
|
||||
&Z_DEVICE_STATE_NAME(dev_name)); \
|
||||
NET_L2_DATA_INIT(dev_name, 0, l2_ctx_type); \
|
||||
NET_IF_INIT(dev_name, 0, l2, mtu, NET_IF_MAX_CONFIGS)
|
||||
|
||||
|
@ -2328,9 +2330,10 @@ struct net_if_api {
|
|||
instance, init_fn, pm_action_cb, \
|
||||
data, cfg, prio, api, l2, \
|
||||
l2_ctx_type, mtu) \
|
||||
Z_DEVICE_STATE_DEFINE(node_id, dev_name) \
|
||||
Z_DEVICE_DEFINE(node_id, dev_name, drv_name, init_fn, \
|
||||
pm_action_cb, data, cfg, POST_KERNEL, \
|
||||
prio, api); \
|
||||
prio, api, &Z_DEVICE_STATE_NAME(dev_name)); \
|
||||
NET_L2_DATA_INIT(dev_name, instance, l2_ctx_type); \
|
||||
NET_IF_INIT(dev_name, instance, l2, mtu, NET_IF_MAX_CONFIGS)
|
||||
|
||||
|
@ -2417,8 +2420,10 @@ struct net_if_api {
|
|||
#define Z_NET_DEVICE_OFFLOAD_INIT(node_id, dev_name, drv_name, init_fn, \
|
||||
pm_action_cb, data, cfg, prio, \
|
||||
api, mtu) \
|
||||
Z_DEVICE_STATE_DEFINE(node_id, dev_name) \
|
||||
Z_DEVICE_DEFINE(node_id, dev_name, drv_name, init_fn, \
|
||||
pm_action_cb, data, cfg, POST_KERNEL, prio, api);\
|
||||
pm_action_cb, data, cfg, POST_KERNEL, prio, api, \
|
||||
&Z_DEVICE_STATE_NAME(dev_name)); \
|
||||
NET_IF_OFFLOAD_INIT(dev_name, 0, mtu)
|
||||
|
||||
/**
|
||||
|
|
|
@ -373,7 +373,7 @@ struct stats_hdr *stats_group_find(const char *name);
|
|||
#define STATS_NAME_MAP_NAME(sectname__) stats_map_ ## sectname__
|
||||
|
||||
#define STATS_NAME_START(sectname__) \
|
||||
const struct stats_name_map STATS_NAME_MAP_NAME(sectname__)[] = {
|
||||
static const struct stats_name_map STATS_NAME_MAP_NAME(sectname__)[] = {
|
||||
|
||||
#define STATS_NAME(sectname__, entry__) \
|
||||
{ offsetof(STATS_SECT_DECL(sectname__), entry__), #entry__ },
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue