Skip to content

Commit 24de607

Browse files
GTLin08henrikbrixandersen
authored andcommitted
drivers/i2c: it8xxx2: Allow I2C target entry power saving mode
Add I2C_TARGET_ALLOW_POWER_SAVING config. Enable this config makes I2C target device can enter Doze/Deep doze states while the bus is idle. Ongoing transfers will block low-power entry until they are completed, ensuring correct communication while still reducing overall power consumption. Signed-off-by: Tim Lin <[email protected]>
1 parent 4b8a190 commit 24de607

File tree

4 files changed

+134
-2
lines changed

4 files changed

+134
-2
lines changed

drivers/i2c/Kconfig.it8xxx2

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,14 @@ config I2C_TARGET_IT8XXX2_MAX_BUF_SIZE
7171
config I2C_TARGET_BUFFER_MODE
7272
default y
7373

74+
config I2C_TARGET_ALLOW_POWER_SAVING
75+
bool "Allow I2C target to enter power-saving modes [EXPERIMENTAL]"
76+
select EXPERIMENTAL
77+
depends on !SOC_IT8XXX2_REG_SET_V1
78+
help
79+
When enabled, the I2C target device can enter Doze/Deep doze states
80+
while the bus is idle. Ongoing transfers will block low-power entry
81+
until they are completed, ensuring correct communication while still
82+
reducing overall power consumption.
83+
7484
endif # I2C_TARGET

drivers/i2c/i2c_ite_enhance.c

Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <zephyr/drivers/pinctrl.h>
1212
#include <zephyr/irq.h>
1313
#include <zephyr/kernel.h>
14+
#include <zephyr/pm/device.h>
1415
#include <zephyr/pm/policy.h>
1516
#include <errno.h>
1617
#include <soc.h>
@@ -123,6 +124,8 @@ struct i2c_enhance_data {
123124
struct k_mutex mutex;
124125
struct k_sem device_sync_sem;
125126
struct i2c_bitbang bitbang;
127+
struct gpio_callback gpio_wui_scl_cb;
128+
struct gpio_callback gpio_wui_sda_cb;
126129
/* Index into output data */
127130
size_t widx;
128131
/* Index into input data */
@@ -1197,6 +1200,20 @@ static void i2c_enhance_isr(void *arg)
11971200
#endif
11981201
}
11991202

1203+
#ifdef CONFIG_I2C_TARGET_ALLOW_POWER_SAVING
1204+
void wui_scl_isr(const struct device *gpio, struct gpio_callback *cb, uint32_t pins)
1205+
{
1206+
/* Disable interrupts on SCL pin to avoid repeated interrupts. */
1207+
(void)gpio_pin_interrupt_configure(gpio, (find_msb_set(pins) - 1), GPIO_INT_MODE_DISABLED);
1208+
}
1209+
1210+
void wui_sda_isr(const struct device *gpio, struct gpio_callback *cb, uint32_t pins)
1211+
{
1212+
/* Disable interrupts on SDA pin to avoid repeated interrupts. */
1213+
(void)gpio_pin_interrupt_configure(gpio, (find_msb_set(pins) - 1), GPIO_INT_MODE_DISABLED);
1214+
}
1215+
#endif
1216+
12001217
static int i2c_enhance_init(const struct device *dev)
12011218
{
12021219
struct i2c_enhance_data *data = dev->data;
@@ -1286,6 +1303,31 @@ static int i2c_enhance_init(const struct device *dev)
12861303
return status;
12871304
}
12881305

1306+
#ifdef CONFIG_I2C_TARGET_ALLOW_POWER_SAVING
1307+
if (config->target_enable) {
1308+
/*
1309+
* Configure GPIO callbacks for SDA/SCL pins as wake-up sources.
1310+
* When the device enters PM_DEVICE_ACTION_SUSPEND, the pins are
1311+
* set to trigger interrupts on both edges.
1312+
* Any bus activity will wake the system from Deep Doze. Enabling
1313+
* lower power consumption while maintaining reliable communication.
1314+
*/
1315+
gpio_init_callback(&data->gpio_wui_scl_cb, wui_scl_isr, BIT(config->scl_gpios.pin));
1316+
status = gpio_add_callback(config->scl_gpios.port, &data->gpio_wui_scl_cb);
1317+
if (status < 0) {
1318+
LOG_ERR("Failed to add SCL %d wui pin callback (err %d)", config->port,
1319+
status);
1320+
return status;
1321+
}
1322+
gpio_init_callback(&data->gpio_wui_sda_cb, wui_sda_isr, BIT(config->sda_gpios.pin));
1323+
status = gpio_add_callback(config->sda_gpios.port, &data->gpio_wui_sda_cb);
1324+
if (status < 0) {
1325+
LOG_ERR("Failed to add SDA %d wui pin callback (err %d)", config->port,
1326+
status);
1327+
return status;
1328+
}
1329+
}
1330+
#endif
12891331

12901332
return 0;
12911333
}
@@ -1395,8 +1437,10 @@ static int i2c_enhance_target_register(const struct device *dev,
13951437

13961438
/* I2C target initial configuration of PIO mode */
13971439
if (config->target_pio_mode) {
1440+
#ifndef CONFIG_I2C_TARGET_ALLOW_POWER_SAVING
13981441
/* Block to enter power policy. */
13991442
pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
1443+
#endif
14001444

14011445
/* I2C module enable */
14021446
IT8XXX2_I2C_CTR1(base) = IT8XXX2_I2C_MDL_EN;
@@ -1435,13 +1479,15 @@ static int i2c_enhance_target_register(const struct device *dev,
14351479
IT8XXX2_I2C_BYTE_CNT_L(base) =
14361480
CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE & GENMASK(2, 0);
14371481

1482+
#ifndef CONFIG_I2C_TARGET_ALLOW_POWER_SAVING
14381483
/*
14391484
* The EC processor(CPU) cannot be in the k_cpu_idle() and power
14401485
* policy during the transactions with the CQ mode(DMA mode).
14411486
* Otherwise, the EC processor would be clock gated.
14421487
*/
14431488
chip_block_idle();
14441489
pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
1490+
#endif
14451491

14461492
/* I2C module enable and command queue mode */
14471493
IT8XXX2_I2C_CTR1(base) = IT8XXX2_I2C_COMQ_EN | IT8XXX2_I2C_MDL_EN;
@@ -1465,11 +1511,13 @@ static int i2c_enhance_target_unregister(const struct device *dev,
14651511

14661512
irq_disable(config->i2c_irq_base);
14671513

1514+
#ifndef CONFIG_I2C_TARGET_ALLOW_POWER_SAVING
14681515
/* Permit to enter power policy and idle mode. */
14691516
pm_policy_state_lock_put(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
14701517
if (!config->target_pio_mode) {
14711518
chip_permit_idle();
14721519
}
1520+
#endif
14731521

14741522
data->target_cfg = NULL;
14751523
data->target_attached = false;
@@ -1479,6 +1527,59 @@ static int i2c_enhance_target_unregister(const struct device *dev,
14791527
}
14801528
#endif
14811529

1530+
#ifdef CONFIG_I2C_TARGET_ALLOW_POWER_SAVING
1531+
static inline int i2c_enhance_pm_action(const struct device *dev, enum pm_device_action action)
1532+
{
1533+
const struct i2c_enhance_config *const config = dev->config;
1534+
int ret;
1535+
1536+
if (config->target_enable) {
1537+
switch (action) {
1538+
/* Next device power state is in active. */
1539+
case PM_DEVICE_ACTION_RESUME:
1540+
/* Disable interrupts on SCL/SDA pin to avoid repeated interrupts. */
1541+
ret = gpio_pin_interrupt_configure_dt(&config->scl_gpios,
1542+
GPIO_INT_MODE_DISABLED);
1543+
if (ret < 0) {
1544+
LOG_ERR("Failed to configure I2C%d WUI (ret %d)", config->port,
1545+
ret);
1546+
return ret;
1547+
}
1548+
ret = gpio_pin_interrupt_configure_dt(&config->sda_gpios,
1549+
GPIO_INT_MODE_DISABLED);
1550+
if (ret < 0) {
1551+
LOG_ERR("Failed to configure I2C%d WUI (ret %d)", config->port,
1552+
ret);
1553+
return ret;
1554+
}
1555+
break;
1556+
/* Next device power state is deep doze mode */
1557+
case PM_DEVICE_ACTION_SUSPEND:
1558+
/* Configure wakeup pins as both edge tribbers */
1559+
ret = gpio_pin_interrupt_configure_dt(
1560+
&config->scl_gpios, GPIO_INT_MODE_EDGE | GPIO_INT_TRIG_BOTH);
1561+
if (ret < 0) {
1562+
LOG_ERR("Failed to configure I2C%d WUI (ret %d)", config->port,
1563+
ret);
1564+
return ret;
1565+
}
1566+
ret = gpio_pin_interrupt_configure_dt(
1567+
&config->sda_gpios, GPIO_INT_MODE_EDGE | GPIO_INT_TRIG_BOTH);
1568+
if (ret < 0) {
1569+
LOG_ERR("Failed to configure I2C%d WUI (ret %d)", config->port,
1570+
ret);
1571+
return ret;
1572+
}
1573+
break;
1574+
default:
1575+
return -ENOTSUP;
1576+
}
1577+
}
1578+
1579+
return 0;
1580+
}
1581+
#endif /* CONFIG_I2C_TARGET_ALLOW_POWER_SAVING */
1582+
14821583
static DEVICE_API(i2c, i2c_enhance_driver_api) = {
14831584
.configure = i2c_enhance_configure,
14841585
.get_config = i2c_enhance_get_config,
@@ -1530,9 +1631,11 @@ BUILD_ASSERT(IS_ENABLED(CONFIG_I2C_TARGET_BUFFER_MODE),
15301631
}; \
15311632
\
15321633
static struct i2c_enhance_data i2c_enhance_data_##inst; \
1533-
\
1634+
IF_ENABLED(CONFIG_I2C_TARGET_ALLOW_POWER_SAVING, \
1635+
(PM_DEVICE_DT_INST_DEFINE(inst, i2c_enhance_pm_action);)) \
15341636
I2C_DEVICE_DT_INST_DEFINE(inst, i2c_enhance_init, \
1535-
NULL, \
1637+
COND_CODE_1(CONFIG_I2C_TARGET_ALLOW_POWER_SAVING, \
1638+
(PM_DEVICE_DT_INST_GET(inst)), (NULL)), \
15361639
&i2c_enhance_data_##inst, \
15371640
&i2c_enhance_cfg_##inst, \
15381641
POST_KERNEL, \

soc/ite/ec/it8xxx2/chip_chipregs.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,6 +1323,15 @@ enum chip_pll_mode {
13231323
#define IT8XXX2_SMB_PREDEN BIT(7)
13241324
#endif
13251325

1326+
/* SMBus register fields */
1327+
/*
1328+
* it81xx2cx/dx:
1329+
* bit3@0xf01c20: Gating Enable of I2C Idle to Sleep Control
1330+
* it82xx2:
1331+
* bit3@0xf01c09: Gating Enable of I2C Idle to Sleep Control
1332+
*/
1333+
#define IT8XXX2_SMB_GEOIITSC BIT(3)
1334+
13261335
/**
13271336
* Enhanced SMBus/I2C Interface
13281337
* Ch_D: 0x00F03680, Ch_E: 0x00F03500, Ch_F: 0x00F03580

soc/ite/ec/it8xxx2/soc.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,11 @@ void riscv_idle(enum chip_pll_mode mode, unsigned int key)
327327
*/
328328
espi_ite_ec_enable_trans_irq(ESPI_ITE_SOC_DEV, true);
329329
#endif
330+
331+
#if defined(CONFIG_I2C_TARGET) && defined(CONFIG_I2C_ITE_ENHANCE)
332+
/* All I2C Channel idle state will affect CPU entering sleep */
333+
IT8XXX2_SMB_SMB01CHS |= IT8XXX2_SMB_GEOIITSC;
334+
#endif
330335
/* Chip doze after wfi instruction */
331336
chip_pll_ctrl(mode);
332337

@@ -351,6 +356,11 @@ void riscv_idle(enum chip_pll_mode mode, unsigned int key)
351356
}
352357
}
353358

359+
#if defined(CONFIG_I2C_TARGET) && defined(CONFIG_I2C_ITE_ENHANCE)
360+
/* All I2C Channel idle state will not affect CPU entering sleep */
361+
IT8XXX2_SMB_SMB01CHS &= ~IT8XXX2_SMB_GEOIITSC;
362+
#endif
363+
354364
#ifdef CONFIG_ESPI
355365
/* CPU has been woken up, the interrupt is no longer needed */
356366
espi_ite_ec_enable_trans_irq(ESPI_ITE_SOC_DEV, false);

0 commit comments

Comments
 (0)