11
11
#include <zephyr/drivers/pinctrl.h>
12
12
#include <zephyr/irq.h>
13
13
#include <zephyr/kernel.h>
14
+ #include <zephyr/pm/device.h>
14
15
#include <zephyr/pm/policy.h>
15
16
#include <errno.h>
16
17
#include <soc.h>
@@ -123,6 +124,8 @@ struct i2c_enhance_data {
123
124
struct k_mutex mutex ;
124
125
struct k_sem device_sync_sem ;
125
126
struct i2c_bitbang bitbang ;
127
+ struct gpio_callback gpio_wui_scl_cb ;
128
+ struct gpio_callback gpio_wui_sda_cb ;
126
129
/* Index into output data */
127
130
size_t widx ;
128
131
/* Index into input data */
@@ -1197,6 +1200,20 @@ static void i2c_enhance_isr(void *arg)
1197
1200
#endif
1198
1201
}
1199
1202
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
+
1200
1217
static int i2c_enhance_init (const struct device * dev )
1201
1218
{
1202
1219
struct i2c_enhance_data * data = dev -> data ;
@@ -1286,6 +1303,31 @@ static int i2c_enhance_init(const struct device *dev)
1286
1303
return status ;
1287
1304
}
1288
1305
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
1289
1331
1290
1332
return 0 ;
1291
1333
}
@@ -1395,8 +1437,10 @@ static int i2c_enhance_target_register(const struct device *dev,
1395
1437
1396
1438
/* I2C target initial configuration of PIO mode */
1397
1439
if (config -> target_pio_mode ) {
1440
+ #ifndef CONFIG_I2C_TARGET_ALLOW_POWER_SAVING
1398
1441
/* Block to enter power policy. */
1399
1442
pm_policy_state_lock_get (PM_STATE_STANDBY , PM_ALL_SUBSTATES );
1443
+ #endif
1400
1444
1401
1445
/* I2C module enable */
1402
1446
IT8XXX2_I2C_CTR1 (base ) = IT8XXX2_I2C_MDL_EN ;
@@ -1435,13 +1479,15 @@ static int i2c_enhance_target_register(const struct device *dev,
1435
1479
IT8XXX2_I2C_BYTE_CNT_L (base ) =
1436
1480
CONFIG_I2C_TARGET_IT8XXX2_MAX_BUF_SIZE & GENMASK (2 , 0 );
1437
1481
1482
+ #ifndef CONFIG_I2C_TARGET_ALLOW_POWER_SAVING
1438
1483
/*
1439
1484
* The EC processor(CPU) cannot be in the k_cpu_idle() and power
1440
1485
* policy during the transactions with the CQ mode(DMA mode).
1441
1486
* Otherwise, the EC processor would be clock gated.
1442
1487
*/
1443
1488
chip_block_idle ();
1444
1489
pm_policy_state_lock_get (PM_STATE_STANDBY , PM_ALL_SUBSTATES );
1490
+ #endif
1445
1491
1446
1492
/* I2C module enable and command queue mode */
1447
1493
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,
1465
1511
1466
1512
irq_disable (config -> i2c_irq_base );
1467
1513
1514
+ #ifndef CONFIG_I2C_TARGET_ALLOW_POWER_SAVING
1468
1515
/* Permit to enter power policy and idle mode. */
1469
1516
pm_policy_state_lock_put (PM_STATE_STANDBY , PM_ALL_SUBSTATES );
1470
1517
if (!config -> target_pio_mode ) {
1471
1518
chip_permit_idle ();
1472
1519
}
1520
+ #endif
1473
1521
1474
1522
data -> target_cfg = NULL ;
1475
1523
data -> target_attached = false;
@@ -1479,6 +1527,59 @@ static int i2c_enhance_target_unregister(const struct device *dev,
1479
1527
}
1480
1528
#endif
1481
1529
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
+
1482
1583
static DEVICE_API (i2c , i2c_enhance_driver_api ) = {
1483
1584
.configure = i2c_enhance_configure ,
1484
1585
.get_config = i2c_enhance_get_config ,
@@ -1530,9 +1631,11 @@ BUILD_ASSERT(IS_ENABLED(CONFIG_I2C_TARGET_BUFFER_MODE),
1530
1631
}; \
1531
1632
\
1532
1633
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);)) \
1534
1636
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)), \
1536
1639
&i2c_enhance_data_##inst, \
1537
1640
&i2c_enhance_cfg_##inst, \
1538
1641
POST_KERNEL, \
0 commit comments