drivers: i2c: smartbond: Add support for PM

This commit should add all the functionality needed for the I2C
driver to work when PM is enabled.

Signed-off-by: Ioannis Karachalios <ioannis.karachalios.px@renesas.com>
This commit is contained in:
Ioannis Karachalios 2024-05-01 12:57:48 +03:00 committed by Henrik Brix Andersen
commit c140053654
4 changed files with 134 additions and 7 deletions

View file

@ -24,6 +24,15 @@
}; };
}; };
/omit-if-no-ref/ i2c_sleep: i2c_sleep {
group1 {
pinmux = <SMARTBOND_PINMUX(GPIO, 0, 31)>,
<SMARTBOND_PINMUX(GPIO, 0, 30)>;
bias-pull-up;
};
};
i2c2_default: i2c2_default { i2c2_default: i2c2_default {
group1 { group1 {
pinmux = <SMARTBOND_PINMUX(I2C2_SDA, 0, 28)>, pinmux = <SMARTBOND_PINMUX(I2C2_SDA, 0, 28)>,
@ -32,6 +41,14 @@
}; };
}; };
/omit-if-no-ref/ i2c2_sleep: i2c2_sleep {
group1 {
pinmux = <SMARTBOND_PINMUX(GPIO, 0, 28)>,
<SMARTBOND_PINMUX(GPIO, 0, 29)>;
bias-pull-up;
};
};
display_controller_default: display_controller_default { display_controller_default: display_controller_default {
group1 { group1 {
pinmux = <SMARTBOND_PINMUX(LCD, 1, 2)>, pinmux = <SMARTBOND_PINMUX(LCD, 1, 2)>,

View file

@ -153,13 +153,15 @@ zephyr_udc0: &usbd {
&i2c { &i2c {
status = "okay"; status = "okay";
pinctrl-0 = <&i2c_default>; pinctrl-0 = <&i2c_default>;
pinctrl-names = "default"; pinctrl-1 = <&i2c_sleep>;
pinctrl-names = "default", "sleep";
}; };
&i2c2 { &i2c2 {
status = "okay"; status = "okay";
pinctrl-0 = <&i2c2_default>; pinctrl-0 = <&i2c2_default>;
pinctrl-names = "default"; pinctrl-1 = <&i2c2_sleep>;
pinctrl-names = "default", "sleep";
}; };
&spi { &spi {

View file

@ -36,7 +36,7 @@
group1 { group1 {
pinmux = <SMARTBOND_PINMUX(GPIO, 0, 19)>, pinmux = <SMARTBOND_PINMUX(GPIO, 0, 19)>,
<SMARTBOND_PINMUX(GPIO, 0, 18)>; <SMARTBOND_PINMUX(GPIO, 0, 18)>;
bias-pull-down; bias-pull-up;
}; };
}; };
}; };

View file

@ -11,6 +11,10 @@
#include <zephyr/drivers/i2c.h> #include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/pinctrl.h> #include <zephyr/drivers/pinctrl.h>
#include <DA1469xAB.h> #include <DA1469xAB.h>
#include <da1469x_pd.h>
#include <zephyr/pm/device.h>
#include <zephyr/pm/policy.h>
#include <zephyr/pm/device_runtime.h>
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(i2c_smartbond); LOG_MODULE_REGISTER(i2c_smartbond);
@ -31,8 +35,36 @@ struct i2c_smartbond_data {
uint32_t transmit_cnt, receive_cnt; uint32_t transmit_cnt, receive_cnt;
i2c_callback_t cb; i2c_callback_t cb;
void *userdata; void *userdata;
#if defined(CONFIG_PM_DEVICE) || defined(CONFIG_PM_DEVICE_RUNTIME)
ATOMIC_DEFINE(pm_policy_state_flag, 1);
#endif
}; };
static inline void i2c_smartbond_pm_policy_state_lock_get(struct i2c_smartbond_data *data)
{
#if defined(CONFIG_PM_DEVICE) || defined(CONFIG_PM_DEVICE_RUNTIME)
if (atomic_test_and_set_bit(data->pm_policy_state_flag, 0) == 0) {
/*
* Prevent the SoC from etering the normal sleep state as PDC does not support
* waking up the application core following I2C events.
*/
pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
}
#endif
}
static inline void i2c_smartbond_pm_policy_state_lock_put(struct i2c_smartbond_data *data)
{
#if defined(CONFIG_PM_DEVICE) || defined(CONFIG_PM_DEVICE_RUNTIME)
if (atomic_test_and_clear_bit(data->pm_policy_state_flag, 0) == 1) {
/*
* Allow the SoC to enter the nornmal sleep state once I2C transactions are done.
*/
pm_policy_state_lock_put(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
}
#endif
}
static int i2c_smartbond_configure(const struct device *dev, uint32_t dev_config) static int i2c_smartbond_configure(const struct device *dev, uint32_t dev_config)
{ {
const struct i2c_smartbond_cfg *config = dev->config; const struct i2c_smartbond_cfg *config = dev->config;
@ -149,6 +181,7 @@ static inline void i2c_smartbond_set_target_address(const struct i2c_smartbond_c
} else { } else {
config->regs->I2C_CON_REG &= ~(I2C_I2C_CON_REG_I2C_10BITADDR_MASTER_Msk); config->regs->I2C_CON_REG &= ~(I2C_I2C_CON_REG_I2C_10BITADDR_MASTER_Msk);
} }
/* Change the Target Address */ /* Change the Target Address */
config->regs->I2C_TAR_REG = ((config->regs->I2C_TAR_REG & ~I2C_I2C_TAR_REG_IC_TAR_Msk) | config->regs->I2C_TAR_REG = ((config->regs->I2C_TAR_REG & ~I2C_I2C_TAR_REG_IC_TAR_Msk) |
(addr & I2C_I2C_TAR_REG_IC_TAR_Msk)); (addr & I2C_I2C_TAR_REG_IC_TAR_Msk));
@ -294,6 +327,8 @@ static int i2c_smartbond_transfer(const struct device *dev, struct i2c_msg *msgs
return ret; return ret;
} }
i2c_smartbond_pm_policy_state_lock_get(data);
for (; data->num_msgs > 0; data->num_msgs--, data->msgs++) { for (; data->num_msgs > 0; data->num_msgs--, data->msgs++) {
data->transmit_cnt = 0; data->transmit_cnt = 0;
data->receive_cnt = 0; data->receive_cnt = 0;
@ -323,6 +358,7 @@ static int i2c_smartbond_transfer(const struct device *dev, struct i2c_msg *msgs
} }
finish: finish:
i2c_smartbond_pm_policy_state_lock_put(data);
return ret; return ret;
} }
@ -377,6 +413,8 @@ static int i2c_smartbond_transfer_cb(const struct device *dev, struct i2c_msg *m
return ret; return ret;
} }
i2c_smartbond_pm_policy_state_lock_get(data);
i2c_smartbond_enable_msg_interrupts(config, data); i2c_smartbond_enable_msg_interrupts(config, data);
LOG_INF("async transfer started"); LOG_INF("async transfer started");
@ -441,6 +479,7 @@ static inline void i2c_smartbond_async_msg_done(const struct device *dev)
data->cb = NULL; data->cb = NULL;
LOG_INF("async transfer finished"); LOG_INF("async transfer finished");
cb(dev, 0, data->userdata); cb(dev, 0, data->userdata);
i2c_smartbond_pm_policy_state_lock_put(data);
} }
} }
@ -493,7 +532,7 @@ static const struct i2c_driver_api i2c_smartbond_driver_api = {
#endif #endif
}; };
static int i2c_smartbond_init(const struct device *dev) static int i2c_smartbond_resume(const struct device *dev)
{ {
const struct i2c_smartbond_cfg *config = dev->config; const struct i2c_smartbond_cfg *config = dev->config;
int err; int err;
@ -515,7 +554,75 @@ static int i2c_smartbond_init(const struct device *dev)
I2C_MODE_CONTROLLER | i2c_map_dt_bitrate(config->bitrate)); I2C_MODE_CONTROLLER | i2c_map_dt_bitrate(config->bitrate));
} }
#if defined(CONFIG_PM_DEVICE) || defined(CONFIG_PM_DEVICE_RUNTIME)
static int i2c_smartbond_suspend(const struct device *dev)
{
int ret;
const struct i2c_smartbond_cfg *config = dev->config;
/* Disable the I2C digital block */
config->regs->I2C_ENABLE_REG &= ~I2C_I2C_ENABLE_REG_I2C_EN_Msk;
/* Gate I2C clocking */
CRG_COM->RESET_CLK_COM_REG = config->periph_clock_config;
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP);
if (ret < 0) {
LOG_WRN("Fialid to configure the I2C pins to inactive state");
}
return ret;
}
static int i2c_smartbond_pm_action(const struct device *dev,
enum pm_device_action action)
{
int ret = 0;
switch (action) {
case PM_DEVICE_ACTION_RESUME:
/*
* Although the GPIO driver should already be initialized, make sure PD_COM
* is up and running before accessing the I2C block.
*/
da1469x_pd_acquire(MCU_PD_DOMAIN_COM);
ret = i2c_smartbond_resume(dev);
break;
case PM_DEVICE_ACTION_SUSPEND:
ret = i2c_smartbond_suspend(dev);
/*
* Once the I2C block is turned off its power domain can
* be released, as well.
*/
da1469x_pd_release(MCU_PD_DOMAIN_COM);
break;
default:
return -ENOTSUP;
}
return ret;
}
#endif
static int i2c_smartbond_init(const struct device *dev)
{
int ret;
#ifdef CONFIG_PM_DEVICE_RUNTIME
/* Make sure device state is marked as suspended */
pm_device_init_suspended(dev);
ret = pm_device_runtime_enable(dev);
#else
da1469x_pd_acquire(MCU_PD_DOMAIN_COM);
ret = i2c_smartbond_resume(dev);
#endif
return ret;
}
#define I2C_SMARTBOND_DEVICE(id) \ #define I2C_SMARTBOND_DEVICE(id) \
PM_DEVICE_DT_INST_DEFINE(id, i2c_smartbond_pm_action); \
PINCTRL_DT_INST_DEFINE(id); \ PINCTRL_DT_INST_DEFINE(id); \
static const struct i2c_smartbond_cfg i2c_smartbond_##id##_cfg = { \ static const struct i2c_smartbond_cfg i2c_smartbond_##id##_cfg = { \
.regs = (I2C_Type *)DT_INST_REG_ADDR(id), \ .regs = (I2C_Type *)DT_INST_REG_ADDR(id), \
@ -529,8 +636,9 @@ static int i2c_smartbond_init(const struct device *dev)
I2C_SMARTBOND_CONFIGURE(id); \ I2C_SMARTBOND_CONFIGURE(id); \
return ret; \ return ret; \
} \ } \
I2C_DEVICE_DT_INST_DEFINE(id, i2c_smartbond_##id##_init, NULL, &i2c_smartbond_##id##_data, \ I2C_DEVICE_DT_INST_DEFINE(id, i2c_smartbond_##id##_init, PM_DEVICE_DT_INST_GET(id), \
&i2c_smartbond_##id##_cfg, POST_KERNEL, \ &i2c_smartbond_##id##_data, \
CONFIG_I2C_INIT_PRIORITY, &i2c_smartbond_driver_api); &i2c_smartbond_##id##_cfg, POST_KERNEL, \
CONFIG_I2C_INIT_PRIORITY, &i2c_smartbond_driver_api);
DT_INST_FOREACH_STATUS_OKAY(I2C_SMARTBOND_DEVICE) DT_INST_FOREACH_STATUS_OKAY(I2C_SMARTBOND_DEVICE)