drivers: entropy: cc13xx_cc26xx: add power management support
This commit adds a dependency on the TRNG resource in the TI Power
module, prevents the system policy from entering standby when TRNG is
active, and adds support for device PM.
Signed-off-by: Vincent Wan <vincent.wan@linaro.org>
diff --git a/drivers/entropy/entropy_cc13xx_cc26xx.c b/drivers/entropy/entropy_cc13xx_cc26xx.c
index 9431509..72d34e9 100644
--- a/drivers/entropy/entropy_cc13xx_cc26xx.c
+++ b/drivers/entropy/entropy_cc13xx_cc26xx.c
@@ -10,12 +10,17 @@
#include <device.h>
#include <drivers/entropy.h>
#include <irq.h>
+#include <power/power.h>
+
#include <sys/ring_buffer.h>
#include <sys/sys_io.h>
#include <driverlib/prcm.h>
#include <driverlib/trng.h>
+#include <ti/drivers/Power.h>
+#include <ti/drivers/power/PowerCC26X2.h>
+
#define CPU_FREQ DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency)
#define US_PER_SAMPLE (1000000ULL * \
@@ -26,6 +31,13 @@
struct k_sem sync;
struct ring_buf pool;
uint8_t data[CONFIG_ENTROPY_CC13XX_CC26XX_POOL_SIZE];
+#ifdef CONFIG_SYS_POWER_MANAGEMENT
+ Power_NotifyObj post_notify;
+ bool constrained;
+#endif
+#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
+ uint32_t pm_state;
+#endif
};
DEVICE_DECLARE(entropy_cc13xx_cc26xx);
@@ -36,6 +48,41 @@
return dev->driver_data;
}
+static void start_trng(struct entropy_cc13xx_cc26xx_data *data)
+{
+ /* Initialization as described in TRM section 18.6.1.2 */
+ TRNGReset();
+ while (sys_read32(TRNG_BASE + TRNG_O_SWRESET)) {
+ continue;
+ }
+
+ /* Set samples per cycle */
+ TRNGConfigure(0, CONFIG_ENTROPY_CC13XX_CC26XX_SAMPLES_PER_CYCLE,
+ 0);
+ /* De-tune FROs */
+ sys_write32(TRNG_FRODETUNE_FRO_MASK_M, TRNG_BASE +
+ TRNG_O_FRODETUNE);
+ /* Enable FROs */
+ sys_write32(TRNG_FROEN_FRO_MASK_M, TRNG_BASE + TRNG_O_FROEN);
+ /* Set shutdown and alarm thresholds */
+ sys_write32((CONFIG_ENTROPY_CC13XX_CC26XX_SHUTDOWN_THRESHOLD
+ << 16) | CONFIG_ENTROPY_CC13XX_CC26XX_ALARM_THRESHOLD,
+ TRNG_BASE + TRNG_O_ALARMCNT);
+
+ TRNGEnable();
+ TRNGIntEnable(TRNG_NUMBER_READY | TRNG_FRO_SHUTDOWN);
+}
+
+#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
+static void stop_trng(struct entropy_cc13xx_cc26xx_data *data)
+{
+ TRNGDisable();
+
+ TRNGIntClear(TRNG_NUMBER_READY | TRNG_FRO_SHUTDOWN);
+ TRNGIntDisable(TRNG_NUMBER_READY | TRNG_FRO_SHUTDOWN);
+}
+#endif
+
static void handle_shutdown_ovf(void)
{
uint32_t off;
@@ -59,6 +106,17 @@
struct entropy_cc13xx_cc26xx_data *data = get_dev_data(dev);
uint32_t cnt;
+#if defined(CONFIG_SYS_POWER_MANAGEMENT) && \
+ defined(CONFIG_SYS_POWER_SLEEP_STATES)
+ unsigned int key = irq_lock();
+
+ if (!data->constrained) {
+ sys_pm_ctrl_disable_state(SYS_POWER_STATE_SLEEP_2);
+ data->constrained = true;
+ }
+ irq_unlock(key);
+#endif
+
TRNGIntEnable(TRNG_NUMBER_READY);
while (len) {
@@ -80,7 +138,8 @@
static void entropy_cc13xx_cc26xx_isr(void *arg)
{
struct entropy_cc13xx_cc26xx_data *data = get_dev_data(arg);
- uint32_t src, cnt;
+ uint32_t src = 0;
+ uint32_t cnt;
uint32_t num[2];
/* Interrupt service routine as described in TRM section 18.6.1.3.2 */
@@ -95,6 +154,14 @@
/* When pool is full disable interrupt and stop reading numbers */
if (cnt != sizeof(num)) {
+#if defined(CONFIG_SYS_POWER_MANAGEMENT) && \
+ defined(CONFIG_SYS_POWER_SLEEP_STATES)
+ if (data->constrained) {
+ sys_pm_ctrl_enable_state(
+ SYS_POWER_STATE_SLEEP_2);
+ data->constrained = false;
+ }
+#endif
TRNGIntDisable(TRNG_NUMBER_READY);
}
@@ -123,7 +190,9 @@
cnt = ring_buf_get(&data->pool, buf, len);
irq_unlock(key);
- if ((cnt != len) && ((flags & ENTROPY_BUSYWAIT) != 0U)) {
+ if ((cnt == len) || ((flags & ENTROPY_BUSYWAIT) == 0U)) {
+ read = cnt;
+ } else {
buf += cnt;
len -= cnt;
@@ -165,17 +234,114 @@
}
}
- } else {
- read = cnt;
}
return read;
}
+#ifdef CONFIG_SYS_POWER_MANAGEMENT
+/*
+ * ======== post_notify_fxn ========
+ * Called by Power module when waking up the CPU from Standby. The TRNG needs
+ * to be reconfigured afterwards, unless Zephyr's device PM turned it off, in
+ * which case it'd be responsible for turning it back on and reconfiguring it.
+ */
+static int post_notify_fxn(unsigned int eventType, uintptr_t eventArg,
+ uintptr_t clientArg)
+{
+ struct device *dev = (struct device *)clientArg;
+ int ret = Power_NOTIFYDONE;
+ int16_t res_id;
+
+ /* Reconfigure the hardware if returning from sleep */
+ if (eventType == PowerCC26XX_AWAKE_STANDBY) {
+ res_id = PowerCC26XX_PERIPH_TRNG;
+
+ if (Power_getDependencyCount(res_id) != 0) {
+ /* Reconfigure and enable TRNG only if powered */
+ start_trng(get_dev_data(dev));
+ }
+ }
+
+ return (ret);
+}
+#endif
+
+#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
+static int entropy_cc13xx_cc26xx_set_power_state(struct device *dev,
+ uint32_t new_state)
+{
+ struct entropy_cc13xx_cc26xx_data *data = get_dev_data(dev);
+ int ret = 0;
+
+ if ((new_state == DEVICE_PM_ACTIVE_STATE) &&
+ (new_state != data->pm_state)) {
+ Power_setDependency(PowerCC26XX_PERIPH_TRNG);
+ start_trng(data);
+ } else {
+ __ASSERT_NO_MSG(new_state == DEVICE_PM_LOW_POWER_STATE ||
+ new_state == DEVICE_PM_SUSPEND_STATE ||
+ new_state == DEVICE_PM_OFF_STATE);
+
+ if (data->pm_state == DEVICE_PM_ACTIVE_STATE) {
+ stop_trng(data);
+ Power_releaseDependency(PowerCC26XX_PERIPH_TRNG);
+ }
+ }
+ data->pm_state = new_state;
+
+ return ret;
+}
+
+static int entropy_cc13xx_cc26xx_pm_control(struct device *dev,
+ uint32_t ctrl_command, void *context, device_pm_cb cb, void *arg)
+{
+ struct entropy_cc13xx_cc26xx_data *data = get_dev_data(dev);
+ int ret = 0;
+
+ if (ctrl_command == DEVICE_PM_SET_POWER_STATE) {
+ uint32_t new_state = *((const uint32_t *)context);
+
+ if (new_state != data->pm_state) {
+ ret = entropy_cc13xx_cc26xx_set_power_state(dev,
+ new_state);
+ }
+ } else {
+ __ASSERT_NO_MSG(ctrl_command == DEVICE_PM_GET_POWER_STATE);
+ *((uint32_t *)context) = data->pm_state;
+ }
+
+ if (cb) {
+ cb(dev, ret, context, arg);
+ }
+
+ return ret;
+}
+#endif /* CONFIG_DEVICE_POWER_MANAGEMENT */
+
static int entropy_cc13xx_cc26xx_init(struct device *dev)
{
struct entropy_cc13xx_cc26xx_data *data = get_dev_data(dev);
+#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
+ get_dev_data(dev)->pm_state = DEVICE_PM_ACTIVE_STATE;
+#endif
+
+ /* Initialize driver data */
+ ring_buf_init(&data->pool, sizeof(data->data), data->data);
+
+#if defined(CONFIG_SYS_POWER_MANAGEMENT)
+ Power_setDependency(PowerCC26XX_PERIPH_TRNG);
+#if defined(CONFIG_SYS_POWER_SLEEP_STATES)
+ /* Stay out of standby until buffer is filled with entropy */
+ sys_pm_ctrl_disable_state(SYS_POWER_STATE_SLEEP_2);
+ data->constrained = true;
+#endif
+ /* Register notification function */
+ Power_registerNotify(&data->post_notify,
+ PowerCC26XX_AWAKE_STANDBY,
+ post_notify_fxn, (uintptr_t)dev);
+#else
/* Power TRNG domain */
PRCMPowerDomainOn(PRCM_DOMAIN_PERIPH);
@@ -199,29 +365,9 @@
PRCM_DOMAIN_POWER_ON) {
continue;
}
+#endif
- /* Initialize driver data */
- ring_buf_init(&data->pool, sizeof(data->data), data->data);
-
- /* Initialization as described in TRM section 18.6.1.2 */
- TRNGReset();
- while (sys_read32(TRNG_BASE + TRNG_O_SWRESET)) {
- continue;
- }
-
- /* Set samples per cycle */
- TRNGConfigure(0, CONFIG_ENTROPY_CC13XX_CC26XX_SAMPLES_PER_CYCLE, 0);
- /* De-tune FROs */
- sys_write32(TRNG_FRODETUNE_FRO_MASK_M, TRNG_BASE + TRNG_O_FRODETUNE);
- /* Enable FROs */
- sys_write32(TRNG_FROEN_FRO_MASK_M, TRNG_BASE + TRNG_O_FROEN);
- /* Set shutdown and alarm thresholds */
- sys_write32((CONFIG_ENTROPY_CC13XX_CC26XX_SHUTDOWN_THRESHOLD << 16) |
- CONFIG_ENTROPY_CC13XX_CC26XX_ALARM_THRESHOLD,
- TRNG_BASE + TRNG_O_ALARMCNT);
-
- TRNGEnable();
- TRNGIntEnable(TRNG_NUMBER_READY | TRNG_FRO_SHUTDOWN);
+ start_trng(data);
IRQ_CONNECT(DT_INST_IRQN(0),
DT_INST_IRQ(0, priority),
@@ -242,7 +388,16 @@
.sync = Z_SEM_INITIALIZER(entropy_cc13xx_cc26xx_data.sync, 0, 1),
};
+#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
+DEVICE_DEFINE(entropy_cc13xx_cc26xx, DT_INST_LABEL(0),
+ entropy_cc13xx_cc26xx_init,
+ entropy_cc13xx_cc26xx_pm_control,
+ &entropy_cc13xx_cc26xx_data, NULL,
+ PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
+ &entropy_cc13xx_cc26xx_driver_api);
+#else
DEVICE_AND_API_INIT(entropy_cc13xx_cc26xx, DT_INST_LABEL(0),
entropy_cc13xx_cc26xx_init, &entropy_cc13xx_cc26xx_data,
NULL, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&entropy_cc13xx_cc26xx_driver_api);
+#endif