Skip to content

Commit 47f472a

Browse files
MulinChaokartben
authored andcommitted
drivers: i2c: npcx: add support to wake up from sleep mode
Add support to wake up from sleep mode by START condition when i2c is configured to target mode. Signed-off-by: Alvis Sun <yfsun@nuvoton.com> Signed-off-by: Mulin Chao <mlchao@nuvoton.com>
1 parent e2d4b98 commit 47f472a

File tree

5 files changed

+195
-27
lines changed

5 files changed

+195
-27
lines changed

drivers/i2c/i2c_npcx_controller.c

Lines changed: 149 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@
7373
#include <zephyr/kernel.h>
7474
#include <zephyr/sys/atomic.h>
7575
#include <soc.h>
76+
#include "soc_miwu.h"
77+
#include "soc_pins.h"
78+
#include "soc_power.h"
7679

7780
#include <zephyr/logging/log.h>
7881
#include <zephyr/irq.h>
@@ -117,6 +120,11 @@ enum npcx_i2c_flag {
117120
NPCX_I2C_FLAG_COUNT,
118121
};
119122

123+
enum i2c_pm_policy_state_flag {
124+
I2C_PM_POLICY_STATE_FLAG_TGT,
125+
I2C_PM_POLICY_STATE_FLAG_COUNT,
126+
};
127+
120128
/*
121129
* Internal SMBus Interface driver states values, which reflect events
122130
* which occurred on the bus
@@ -145,6 +153,11 @@ struct i2c_ctrl_config {
145153
uintptr_t base; /* i2c controller base address */
146154
struct npcx_clk_cfg clk_cfg; /* clock configuration */
147155
uint8_t irq; /* i2c controller irq */
156+
#ifdef CONFIG_I2C_TARGET
157+
/* i2c wake-up input source configuration */
158+
const struct npcx_wui smb_wui;
159+
bool wakeup_source;
160+
#endif /* CONFIG_I2C_TARGET */
148161
};
149162

150163
/* Driver data */
@@ -164,7 +177,13 @@ struct i2c_ctrl_data {
164177
#ifdef CONFIG_I2C_TARGET
165178
struct i2c_target_config *target_cfg;
166179
atomic_t flags;
167-
#endif
180+
/* i2c wake-up callback configuration */
181+
struct miwu_callback smb_wk_cb;
182+
#endif /* CONFIG_I2C_TARGET */
183+
184+
#if defined(CONFIG_PM) && defined(CONFIG_I2C_TARGET)
185+
ATOMIC_DEFINE(pm_policy_state_flag, I2C_PM_POLICY_STATE_FLAG_COUNT);
186+
#endif /* CONFIG_PM && CONFIG_I2C_TARGET */
168187
};
169188

170189
/* Driver convenience defines */
@@ -184,6 +203,38 @@ static const struct npcx_i2c_timing_cfg npcx_20m_speed_confs[] = {
184203
[NPCX_I2C_BUS_SPEED_1MHZ] = {.HLDT = 7, .k1 = 16, .k2 = 10},
185204
};
186205

206+
#if defined(CONFIG_PM) && defined(CONFIG_I2C_TARGET)
207+
static void i2c_npcx_pm_policy_state_lock_get(const struct device *dev,
208+
enum i2c_pm_policy_state_flag flag)
209+
{
210+
const struct i2c_ctrl_config *const config = dev->config;
211+
struct i2c_ctrl_data *const data = dev->data;
212+
213+
if (!config->wakeup_source) {
214+
return;
215+
}
216+
217+
if (atomic_test_and_set_bit(data->pm_policy_state_flag, flag) == 0) {
218+
pm_policy_state_lock_get(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES);
219+
}
220+
}
221+
222+
static void i2c_npcx_pm_policy_state_lock_put(const struct device *dev,
223+
enum i2c_pm_policy_state_flag flag)
224+
{
225+
const struct i2c_ctrl_config *const config = dev->config;
226+
struct i2c_ctrl_data *const data = dev->data;
227+
228+
if (!config->wakeup_source) {
229+
return;
230+
}
231+
232+
if (atomic_test_and_clear_bit(data->pm_policy_state_flag, flag) == 1) {
233+
pm_policy_state_lock_put(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES);
234+
}
235+
}
236+
#endif /* CONFIG_PM && CONFIG_I2C_TARGET */
237+
187238
/* I2C controller inline functions access shared registers */
188239
static inline void i2c_ctrl_start(const struct device *dev)
189240
{
@@ -779,6 +830,10 @@ static void i2c_ctrl_target_isr(const struct device *dev, uint8_t status)
779830
/* End of transaction */
780831
data->oper_state = NPCX_I2C_IDLE;
781832

833+
#ifdef CONFIG_PM
834+
i2c_npcx_pm_policy_state_lock_put(dev, I2C_PM_POLICY_STATE_FLAG_TGT);
835+
#endif /* CONFIG_PM */
836+
782837
LOG_DBG("target: Bus error on port%02x!", data->port);
783838
return;
784839
}
@@ -793,6 +848,10 @@ static void i2c_ctrl_target_isr(const struct device *dev, uint8_t status)
793848
if (target_cb->stop) {
794849
target_cb->stop(data->target_cfg);
795850
}
851+
852+
#ifdef CONFIG_PM
853+
i2c_npcx_pm_policy_state_lock_put(dev, I2C_PM_POLICY_STATE_FLAG_TGT);
854+
#endif /* CONFIG_PM */
796855
return;
797856
}
798857

@@ -857,7 +916,7 @@ static void i2c_ctrl_target_isr(const struct device *dev, uint8_t status)
857916
status, data->port);
858917
}
859918
}
860-
#endif
919+
#endif /* CONFIG_I2C_TARGET */
861920

862921
/* I2C controller isr function */
863922
static void i2c_ctrl_isr(const struct device *dev)
@@ -874,7 +933,7 @@ static void i2c_ctrl_isr(const struct device *dev)
874933
i2c_ctrl_target_isr(dev, status);
875934
return;
876935
}
877-
#endif
936+
#endif /* CONFIG_I2C_TARGET */
878937

879938
/* A 'Bus Error' has been identified */
880939
if (IS_BIT_SET(status, NPCX_SMBST_BER)) {
@@ -1073,6 +1132,7 @@ int npcx_i2c_ctrl_target_register(const struct device *i2c_dev,
10731132
struct i2c_target_config *target_cfg, uint8_t port)
10741133
{
10751134
struct smb_reg *const inst = HAL_I2C_INSTANCE(i2c_dev);
1135+
const struct i2c_ctrl_config *const config = i2c_dev->config;
10761136
struct i2c_ctrl_data *const data = i2c_dev->data;
10771137
int idx_ctrl = (port & 0xF0) >> 4;
10781138
int idx_port = (port & 0x0F);
@@ -1105,16 +1165,29 @@ int npcx_i2c_ctrl_target_register(const struct device *i2c_dev,
11051165

11061166
/* Reconfigure SMBCTL1 */
11071167
inst->SMBCTL1 |= BIT(NPCX_SMBCTL1_NMINTE) | BIT(NPCX_SMBCTL1_INTEN);
1168+
1169+
/* Enable irq of smb wake-up event */
1170+
if (IS_ENABLED(CONFIG_PM) && config->wakeup_source) {
1171+
1172+
/* Enable SMB wake up detection */
1173+
npcx_i2c_target_start_wk_enable(idx_ctrl, true);
1174+
/* Enable start detect in IDLE */
1175+
inst->SMBCTL3 |= BIT(NPCX_SMBCTL3_IDL_START);
1176+
/* Enable SMB's MIWU interrupts */
1177+
npcx_miwu_irq_enable(&config->smb_wui);
1178+
}
11081179
i2c_ctrl_irq_enable(i2c_dev, 1);
11091180

11101181
return 0;
11111182
}
11121183

11131184
int npcx_i2c_ctrl_target_unregister(const struct device *i2c_dev,
1114-
struct i2c_target_config *target_cfg)
1185+
struct i2c_target_config *target_cfg, uint8_t port)
11151186
{
11161187
struct smb_reg *const inst = HAL_I2C_INSTANCE(i2c_dev);
1188+
const struct i2c_ctrl_config *const config = i2c_dev->config;
11171189
struct i2c_ctrl_data *const data = i2c_dev->data;
1190+
int idx_ctrl = (port & 0xF0) >> 4;
11181191

11191192
/* No I2c module has been configured to target mode */
11201193
if (!atomic_test_bit(&data->flags, NPCX_I2C_FLAG_TARGET)) {
@@ -1139,14 +1212,46 @@ int npcx_i2c_ctrl_target_unregister(const struct device *i2c_dev,
11391212

11401213
/* Reconfigure SMBCTL1 */
11411214
inst->SMBCTL1 |= BIT(NPCX_SMBCTL1_NMINTE) | BIT(NPCX_SMBCTL1_INTEN);
1142-
i2c_ctrl_irq_enable(i2c_dev, 1);
11431215

1216+
/* Disable irq of smb wake-up event */
1217+
if (IS_ENABLED(CONFIG_PM)) {
1218+
/* Disable SMB wake up detection */
1219+
npcx_i2c_target_start_wk_enable(idx_ctrl, false);
1220+
/* Disable start detect in IDLE */
1221+
inst->SMBCTL3 &= ~BIT(NPCX_SMBCTL3_IDL_START);
1222+
/* Disable SMB's MIWU interrupts */
1223+
npcx_miwu_irq_disable(&config->smb_wui);
1224+
1225+
}
1226+
i2c_ctrl_irq_enable(i2c_dev, 1);
11441227
/* Mark it as controller mode */
11451228
atomic_clear_bit(&data->flags, NPCX_I2C_FLAG_TARGET);
11461229

11471230
return 0;
11481231
}
1149-
#endif
1232+
1233+
static void i2c_target_wk_isr(const struct device *dev, struct npcx_wui *wui)
1234+
{
1235+
struct smb_reg *const inst = HAL_I2C_INSTANCE(dev);
1236+
1237+
/* Clear wake up detection event status */
1238+
npcx_i2c_target_clear_detection_event();
1239+
1240+
/* Reconfigure SMBCTL1 */
1241+
inst->SMBCTL1 |= BIT(NPCX_SMBCTL1_NMINTE) | BIT(NPCX_SMBCTL1_INTEN);
1242+
1243+
/*
1244+
* Suspend-to-idle stops SMB module clocks (derived from APB2/APB3), which must remain
1245+
* active during a transaction.
1246+
*
1247+
* This also prevent Sr set pm_policy_state_lock_get() twice.
1248+
* Otherwise, it will cause I2C cannot switch to deep sleep state for the next time.
1249+
*/
1250+
#ifdef CONFIG_PM
1251+
i2c_npcx_pm_policy_state_lock_get(dev, I2C_PM_POLICY_STATE_FLAG_TGT);
1252+
#endif /* CONFIG_PM */
1253+
}
1254+
#endif /* CONFIG_I2C_TARGET */
11501255

11511256
int npcx_i2c_ctrl_transfer(const struct device *i2c_dev, struct i2c_msg *msgs,
11521257
uint8_t num_msgs, uint16_t addr, int port)
@@ -1160,7 +1265,7 @@ int npcx_i2c_ctrl_transfer(const struct device *i2c_dev, struct i2c_msg *msgs,
11601265
if (atomic_test_bit(&data->flags, NPCX_I2C_FLAG_TARGET)) {
11611266
return -EBUSY;
11621267
}
1163-
#endif
1268+
#endif /* CONFIG_I2C_TARGET */
11641269

11651270
/*
11661271
* suspend-to-idle stops SMB module clocks (derived from APB2/APB3), which must remain
@@ -1285,6 +1390,21 @@ static int i2c_ctrl_init(const struct device *dev)
12851390
/* Initialize i2c module */
12861391
i2c_ctrl_init_module(dev);
12871392

1393+
#ifdef CONFIG_I2C_TARGET
1394+
if (IS_ENABLED(CONFIG_PM) && config->wakeup_source) {
1395+
/* Initialize a miwu device input and its callback function */
1396+
npcx_miwu_init_dev_callback(&data->smb_wk_cb, &config->smb_wui,
1397+
i2c_target_wk_isr, dev);
1398+
npcx_miwu_manage_callback(&data->smb_wk_cb, true);
1399+
/*
1400+
* Configure Start condition wake-up configuration of SMB
1401+
* controller.
1402+
*/
1403+
npcx_miwu_interrupt_configure(&config->smb_wui, NPCX_MIWU_MODE_EDGE,
1404+
NPCX_MIWU_TRIG_HIGH);
1405+
}
1406+
#endif /* CONFIG_I2C_TARGET */
1407+
12881408
/* initialize mutex and semaphore for i2c/smb controller */
12891409
k_sem_init(&data->lock_sem, 1, 1);
12901410
k_sem_init(&data->sync_sem, 0, K_SEM_MAX_LIMIT);
@@ -1316,24 +1436,28 @@ static int i2c_ctrl_init(const struct device *dev)
13161436
}
13171437

13181438

1319-
#define NPCX_I2C_CTRL_INIT(inst) \
1320-
NPCX_I2C_CTRL_INIT_FUNC_DECL(inst); \
1321-
\
1322-
static const struct i2c_ctrl_config i2c_ctrl_cfg_##inst = { \
1323-
.base = DT_INST_REG_ADDR(inst), \
1324-
.irq = DT_INST_IRQN(inst), \
1325-
.clk_cfg = NPCX_DT_CLK_CFG_ITEM(inst), \
1326-
}; \
1327-
\
1328-
static struct i2c_ctrl_data i2c_ctrl_data_##inst; \
1329-
\
1330-
DEVICE_DT_INST_DEFINE(inst, \
1331-
NPCX_I2C_CTRL_INIT_FUNC(inst), \
1332-
NULL, \
1333-
&i2c_ctrl_data_##inst, &i2c_ctrl_cfg_##inst, \
1334-
PRE_KERNEL_1, CONFIG_I2C_INIT_PRIORITY, \
1335-
NULL); \
1336-
\
1439+
#define NPCX_I2C_CTRL_INIT(inst) \
1440+
NPCX_I2C_CTRL_INIT_FUNC_DECL(inst); \
1441+
\
1442+
static const struct i2c_ctrl_config i2c_ctrl_cfg_##inst = { \
1443+
.base = DT_INST_REG_ADDR(inst), \
1444+
.irq = DT_INST_IRQN(inst), \
1445+
.clk_cfg = NPCX_DT_CLK_CFG_ITEM(inst), \
1446+
IF_ENABLED(CONFIG_I2C_TARGET, ( \
1447+
.smb_wui = NPCX_DT_WUI_ITEM_BY_NAME(inst, smb_wui), \
1448+
.wakeup_source = DT_INST_PROP_OR(inst, wakeup_source, 0) \
1449+
)) \
1450+
}; \
1451+
\
1452+
static struct i2c_ctrl_data i2c_ctrl_data_##inst; \
1453+
\
1454+
DEVICE_DT_INST_DEFINE(inst, \
1455+
NPCX_I2C_CTRL_INIT_FUNC(inst), \
1456+
NULL, \
1457+
&i2c_ctrl_data_##inst, &i2c_ctrl_cfg_##inst, \
1458+
PRE_KERNEL_1, CONFIG_I2C_INIT_PRIORITY, \
1459+
NULL); \
1460+
\
13371461
NPCX_I2C_CTRL_INIT_FUNC_IMPL(inst)
13381462

13391463
DT_INST_FOREACH_STATUS_OKAY(NPCX_I2C_CTRL_INIT)

drivers/i2c/i2c_npcx_controller.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,14 @@ int npcx_i2c_ctrl_target_register(const struct device *i2c_dev,
9898
*
9999
* @param i2c_dev Pointer to the device structure for i2c controller instance.
100100
* @param target_cfg Config struct used by the i2c target driver
101+
* @param port Port index of selected i2c port.
101102
*
102103
* @retval 0 Is successful
103104
* @retval -EBUSY If i2c transaction is proceeding.
104105
* @retval -EINVAL If parameters are invalid
105106
*/
106107
int npcx_i2c_ctrl_target_unregister(const struct device *i2c_dev,
107-
struct i2c_target_config *target_cfg);
108+
struct i2c_target_config *target_cfg, uint8_t port);
108109

109110
#ifdef __cplusplus
110111
}

drivers/i2c/i2c_npcx_port.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ static int i2c_npcx_target_unregister(const struct device *dev,
171171
return -EIO;
172172
}
173173

174-
return npcx_i2c_ctrl_target_unregister(config->i2c_ctrl, target_cfg);
174+
return npcx_i2c_ctrl_target_unregister(config->i2c_ctrl, target_cfg, config->port);
175175
}
176176
#endif
177177

soc/nuvoton/npcx/common/scfg.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,35 @@ void npcx_pinctrl_i2c_port_sel(int controller, int port)
105105
}
106106
}
107107

108+
void npcx_i2c_target_start_wk_enable(int controller, bool enable)
109+
{
110+
struct glue_reg *const inst_glue = HAL_GLUE_INST();
111+
112+
if (enable) {
113+
/* Clear Start condition detection status */
114+
inst_glue->SMB_SBD |= BIT(controller);
115+
/* Enable wake up event assertion */
116+
inst_glue->SMB_EEN |= BIT(controller);
117+
} else {
118+
/* Disable wake up event assertion */
119+
inst_glue->SMB_EEN &= ~BIT(controller);
120+
}
121+
}
122+
123+
void npcx_i2c_target_clear_detection_event(void)
124+
{
125+
struct glue_reg *const inst_glue = HAL_GLUE_INST();
126+
uint8_t een = inst_glue->SMB_EEN;
127+
uint8_t sbd = inst_glue->SMB_SBD;
128+
129+
/* Clear Start condition detection status */
130+
for (uint8_t i = 0; i < 8; i++) {
131+
if ((een & BIT(i)) != 0 && (sbd & BIT(i)) != 0) {
132+
inst_glue->SMB_SBD |= BIT(i);
133+
}
134+
}
135+
}
136+
108137
int npcx_pinctrl_flash_write_protect_set(void)
109138
{
110139
struct scfg_reg *inst_scfg = HAL_SFCG_INST();

soc/nuvoton/npcx/common/soc_pins.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,20 @@ void npcx_host_interface_sel(enum npcx_hif_type hif_type);
9494
*/
9595
void npcx_i3c_target_sel(uint8_t module, bool enable);
9696

97+
/**
98+
* @brief Enable smb controller wake up event detection in target mode
99+
*
100+
* @param controller i2c controller device
101+
* @param enable True to enable wake up event detection, false to disable.
102+
*/
103+
void npcx_i2c_target_start_wk_enable(int controller, bool enable);
104+
105+
/**
106+
* @brief Clear wake up event detection status in target mode
107+
*
108+
*/
109+
void npcx_i2c_target_clear_detection_event(void);
110+
97111
#ifdef __cplusplus
98112
}
99113
#endif

0 commit comments

Comments
 (0)