Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

drivers: i2c: pca9542a: Initial driver support #2

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/i2c/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ zephyr_library_sources_ifdef(CONFIG_I2C_ENE_KB1200 i2c_ene_kb1200.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_I2C_SWITCH gpio_i2c_switch.c)
zephyr_library_sources_ifdef(CONFIG_I2C_NUMAKER i2c_numaker.c)
zephyr_library_sources_ifdef(CONFIG_I2C_MAX32 i2c_max32.c)
zephyr_library_sources_ifdef(CONFIG_I2C_PCA9542A i2c_pca9542a.c)

zephyr_library_sources_ifdef(CONFIG_I2C_STM32_V1
i2c_ll_stm32_v1.c
Expand Down
1 change: 1 addition & 0 deletions drivers/i2c/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ source "drivers/i2c/Kconfig.numaker"
source "drivers/i2c/Kconfig.mcux"
source "drivers/i2c/Kconfig.ene"
source "drivers/i2c/Kconfig.max32"
source "drivers/i2c/Kconfig.pca9542a"

config I2C_INIT_PRIORITY
int "Init priority"
Expand Down
27 changes: 27 additions & 0 deletions drivers/i2c/Kconfig.pca9542a
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# pca9542a
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If possible uppercase with a 1-line description. I.e. the same as you added below. Not very important though!

# NXP PCA9542A 2-channel multiplexer


# Copyright (c) 2024 tinyVision.ai Inc.
# SPDX-License-Identifier: Apache-2.0

menuconfig I2C_PCA9542A
bool "NXP PCA9542A 2-channel multiplexer"
default y
depends on DT_HAS_NXP_PCA9542A_ENABLED
help
Enable NXP PCA9542A 2-channel multiplexer

if I2C_PCA9542A

config I2C_PCA9542A_ROOT_INIT_PRIO
int "PCA9542A root driver init priority"
default I2C_INIT_PRIORITY
help
Should be lower than `I2C_PCA9542A_CHANNEL_INIT_PRIO`

config I2C_PCA9542A_CHANNEL_INIT_PRIO
int "PCA9542A channel driver init priority"
default I2C_INIT_PRIORITY
help
Should be higher than `I2C_PCA9542A_ROOT_INIT_PRIO`

endif
167 changes: 167 additions & 0 deletions drivers/i2c/i2c_pca9542a.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
*
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove this empty line. Not very important but we could be asked to remove it when submitting it.

* Copyright (c) 2020 Innoseis BV
* Copyright (c) 2024 tinyVision.ai Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT nxp_pca9542a
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/logging/log.h>
#include <stdint.h>
#include <stdio.h>

#define BCD2BIN(bcd) (((10 * ((bcd) >> 4)) + ((bcd) & 0x0F)))

LOG_MODULE_REGISTER(pca9542a, CONFIG_I2C_LOG_LEVEL);

struct pca9542a_root_config {
struct i2c_dt_spec i2c;
uint8_t nchans;
};

struct pca9542a_root_data {
struct k_mutex lock;
uint8_t selected_chan;
};

struct pca9542a_channel_config {
const struct device *root;
uint8_t chan_mask;
};

static inline struct pca9542a_root_data *get_root_data_from_channel(const struct device *dev)
{
const struct pca9542a_channel_config *channel_config = dev->config;

return channel_config->root->data;
}

static inline const struct pca9542a_root_config *
get_root_config_from_channel(const struct device *dev)
{
const struct pca9542a_channel_config *channel_config = dev->config;

return channel_config->root->config;
}

static int pca9542a_configure(const struct device *dev, uint32_t dev_config)
{
const struct pca9542a_root_config *cfg = get_root_config_from_channel(dev);

return i2c_configure(cfg->i2c.bus, dev_config);
}

static int pca9542a_set_channel(const struct device *dev, uint8_t select_mask)
{
int res = 0;
struct pca9542a_root_data *data = dev->data;
const struct pca9542a_root_config *cfg = dev->config;

/* Only select the channel if its different from the last channel */
if (data->selected_chan != select_mask) {
res = i2c_write_dt(&cfg->i2c, &select_mask, 1);
if (res == 0) {
data->selected_chan = select_mask;
} else {
LOG_DBG("pca9542a: failed to set channel");
}
}
return res;
}

static int pca9542a_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs,
uint16_t addr)
{
struct pca9542a_root_data *data = get_root_data_from_channel(dev);
const struct pca9542a_root_config *config = get_root_config_from_channel(dev);
const struct pca9542a_channel_config *down_cfg = dev->config;
int res;

res = k_mutex_lock(&data->lock, K_MSEC(5000));
if (res != 0) {
return res;
}

res = pca9542a_set_channel(down_cfg->root, down_cfg->chan_mask);
if (res != 0) {
goto end_trans;
}

res = i2c_transfer(config->i2c.bus, msgs, num_msgs, addr);

end_trans:
k_mutex_unlock(&data->lock);
return res;
}

static int pca9542a_root_init(const struct device *dev)
{

struct pca9542a_root_data *i2c_pca9542a = dev->data;
const struct pca9542a_root_config *config = dev->config;

printf("inside root init...\n");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looks like some debug printf(). Feel free to convert it to a LOG_DBG("initializing the PCA9542A I2C multiplexer") or even better: remove it altogether. The shell command device list will tell if it ran successfully.

Thanks for this cleanup!

if (!device_is_ready(config->i2c.bus)) {
LOG_ERR("I2C bus %s not ready", config->i2c.bus->name);
return -ENODEV;
}

i2c_pca9542a->selected_chan = 0;

return 0;
}

static int pca9542a_channel_init(const struct device *dev)
{
const struct pca9542a_channel_config *chan_cfg = dev->config;
const struct pca9542a_root_config *root_cfg = get_root_config_from_channel(dev);

if (!device_is_ready(chan_cfg->root)) {
LOG_ERR("I2C mux root %s not ready", chan_cfg->root->name);
return -ENODEV;
}

if (chan_cfg->chan_mask >= BCD2BIN(root_cfg->nchans + 4)) {
LOG_ERR("Wrong DTS address provided for %s", dev->name);
return -EINVAL;
}

return 0;
}

static const struct i2c_driver_api pca9542a_api_funcs = {
.configure = pca9542a_configure,
.transfer = pca9542a_transfer,
};

BUILD_ASSERT(CONFIG_I2C_PCA9542A_CHANNEL_INIT_PRIO > CONFIG_I2C_PCA9542A_ROOT_INIT_PRIO,
"I2C multiplexer channels must be initialized after their root");

#define PCA9542A_CHILD_DEFINE(node_id) \
static const struct pca9542a_channel_config pca9542a_down_config_##node_id = { \
.chan_mask = BCD2BIN(DT_REG_ADDR(node_id) + 4), \
.root = DEVICE_DT_GET(DT_PARENT(node_id)), \
}; \
DEVICE_DT_DEFINE(node_id, pca9542a_channel_init, NULL, NULL, \
&pca9542a_down_config_##node_id, POST_KERNEL, \
CONFIG_I2C_PCA9542A_CHANNEL_INIT_PRIO, &pca9542a_api_funcs);

#define PCA9542A_INIT(n) \
static const struct pca9542a_root_config pca9542a_cfg_##n = { \
.i2c = I2C_DT_SPEC_INST_GET(n), \
.nchans = 2, \
}; \
static struct pca9542a_root_data pca9542a_data_##n = { \
.lock = Z_MUTEX_INITIALIZER(pca9542a_data_##n.lock), \
}; \
I2C_DEVICE_DT_DEFINE(DT_INST(n, nxp_pca9542a), pca9542a_root_init, NULL, \
&pca9542a_data_##n, &pca9542a_cfg_##n, POST_KERNEL, \
CONFIG_I2C_PCA9542A_ROOT_INIT_PRIO, NULL); \
DT_FOREACH_CHILD(DT_INST(n, nxp_pca9542a), PCA9542A_CHILD_DEFINE);

DT_INST_FOREACH_STATUS_OKAY(PCA9542A_INIT)
13 changes: 13 additions & 0 deletions dts/bindings/i2c/nxp,pca9542a.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright (c) 2024 tinyVision.ai Inc.
# SPDX-License-Identifier: Apache-2.0

description: NXP PCA9542A 2-channel multiplexer

compatible: "nxp,pca9542a"

include: i2c-device.yaml

child-binding:
compatible: "nxp,pca9542a-channel"
include: i2c-controller.yaml
on-bus: i2c
Loading