zephyr/drivers/entropy/entropy_cc13xx_cc26xx.c
Tomasz Bursztyka 4dcfb5531c isr: Normalize usage of device instance through ISR
The goal of this patch is to replace the 'void *' parameter by 'struct
device *' if they use such variable or just 'const void *' on all
relevant ISRs

This will avoid not-so-nice const qualifier tweaks when device instances
will be constant.

Note that only the ISR passed to IRQ_CONNECT are of interest here.

In order to do so, the script fix_isr.py below is necessary:

from pathlib import Path
import subprocess
import pickle
import mmap
import sys
import re
import os

cocci_template = """
@r_fix_isr_0
@
type ret_type;
identifier P;
identifier D;
@@
-ret_type <!fn!>(void *P)
+ret_type <!fn!>(const struct device *P)
{
 ...
(
 const struct device *D = (const struct device *)P;
|
 const struct device *D = P;
)
 ...
}

@r_fix_isr_1
@
type ret_type;
identifier P;
identifier D;
@@
-ret_type <!fn!>(void *P)
+ret_type <!fn!>(const struct device *P)
{
 ...
 const struct device *D;
 ...
(
 D = (const struct device *)P;
|
 D = P;
)
 ...
}

@r_fix_isr_2
@
type ret_type;
identifier A;
@@
-ret_type <!fn!>(void *A)
+ret_type <!fn!>(const void *A)
{
 ...
}

@r_fix_isr_3
@
const struct device *D;
@@
-<!fn!>((void *)D);
+<!fn!>(D);

@r_fix_isr_4
@
type ret_type;
identifier D;
identifier P;
@@
-ret_type <!fn!>(const struct device *P)
+ret_type <!fn!>(const struct device *D)
{
 ...
(
-const struct device *D = (const struct device *)P;
|
-const struct device *D = P;
)
 ...
}

@r_fix_isr_5
@
type ret_type;
identifier D;
identifier P;
@@
-ret_type <!fn!>(const struct device *P)
+ret_type <!fn!>(const struct device *D)
{
 ...
-const struct device *D;
...
(
-D = (const struct device *)P;
|
-D = P;
)
 ...
}
"""

def find_isr(fn):
    db = []
    data = None
    start = 0

    try:
        with open(fn, 'r+') as f:
            data = str(mmap.mmap(f.fileno(), 0).read())
    except Exception as e:
        return db

    while True:
        isr = ""
        irq = data.find('IRQ_CONNECT', start)
        while irq > -1:
            p = 1
            arg = 1
            p_o = data.find('(', irq)
            if p_o < 0:
                irq = -1
                break;

            pos = p_o + 1

            while p > 0:
                if data[pos] == ')':
                    p -= 1
                elif data[pos] == '(':
                    p += 1
                elif data[pos] == ',' and p == 1:
                    arg += 1

                if arg == 3:
                    isr += data[pos]

                pos += 1

            isr = isr.strip(',\\n\\t ')
            if isr not in db and len(isr) > 0:
                db.append(isr)

            start = pos
            break

        if irq < 0:
            break

    return db

def patch_isr(fn, isr_list):
    if len(isr_list) <= 0:
        return

    for isr in isr_list:
        tmplt = cocci_template.replace('<!fn!>', isr)
        with open('/tmp/isr_fix.cocci', 'w') as f:
            f.write(tmplt)

        cmd = ['spatch', '--sp-file', '/tmp/isr_fix.cocci', '--in-place', fn]

        subprocess.run(cmd)

def process_files(path):
    if path.is_file() and path.suffix in ['.h', '.c']:
        p = str(path.parent) + '/' + path.name
        isr_list = find_isr(p)
        patch_isr(p, isr_list)
    elif path.is_dir():
        for p in path.iterdir():
            process_files(p)

if len(sys.argv) < 2:
    print("You need to provide a dir/file path")
    sys.exit(1)

process_files(Path(sys.argv[1]))

And is run: ./fix_isr.py <zephyr root directory>

Finally, some files needed manual fixes such.

Fixes #27399

Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
2020-09-02 13:48:13 +02:00

408 lines
10 KiB
C

/*
* Copyright (c) 2019 Brett Witherspoon
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT ti_cc13xx_cc26xx_trng
#include <kernel.h>
#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 * \
CONFIG_ENTROPY_CC13XX_CC26XX_SAMPLES_PER_CYCLE / CPU_FREQ + 1ULL)
struct entropy_cc13xx_cc26xx_data {
struct k_sem lock;
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);
static inline struct entropy_cc13xx_cc26xx_data *
get_dev_data(const struct device *dev)
{
return dev->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;
/* Clear shutdown */
TRNGIntClear(TRNG_FRO_SHUTDOWN);
/* Disabled FROs */
off = sys_read32(TRNG_BASE + TRNG_O_ALARMSTOP);
/* Clear alarms */
sys_write32(0, TRNG_BASE + TRNG_O_ALARMMASK);
sys_write32(0, TRNG_BASE + TRNG_O_ALARMSTOP);
/* De-tune FROs */
sys_write32(off, TRNG_BASE + TRNG_O_FRODETUNE);
/* Re-enable FROs */
sys_write32(off, TRNG_BASE + TRNG_O_FROEN);
}
static int entropy_cc13xx_cc26xx_get_entropy(const struct device *dev,
uint8_t *buf,
uint16_t len)
{
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) {
k_sem_take(&data->lock, K_FOREVER);
cnt = ring_buf_get(&data->pool, buf, len);
k_sem_give(&data->lock);
if (cnt) {
buf += cnt;
len -= cnt;
} else {
k_sem_take(&data->sync, K_FOREVER);
}
}
return 0;
}
static void entropy_cc13xx_cc26xx_isr(const void *arg)
{
struct entropy_cc13xx_cc26xx_data *data = get_dev_data(arg);
uint32_t src = 0;
uint32_t cnt;
uint32_t num[2];
/* Interrupt service routine as described in TRM section 18.6.1.3.2 */
src = TRNGStatusGet();
if (src & TRNG_NUMBER_READY) {
/* This function acknowledges the ready status */
num[1] = TRNGNumberGet(TRNG_HI_WORD);
num[0] = TRNGNumberGet(TRNG_LOW_WORD);
cnt = ring_buf_put(&data->pool, (uint8_t *)num, sizeof(num));
/* 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);
}
k_sem_give(&data->sync);
}
/* Change the shutdown FROs' oscillating frequncy in an attempt to
* prevent further locking on to the sampling clock frequncy.
*/
if (src & TRNG_FRO_SHUTDOWN) {
handle_shutdown_ovf();
}
}
static int entropy_cc13xx_cc26xx_get_entropy_isr(const struct device *dev,
uint8_t *buf, uint16_t len,
uint32_t flags)
{
struct entropy_cc13xx_cc26xx_data *data = get_dev_data(dev);
uint16_t cnt;
uint16_t read = len;
uint32_t src;
uint32_t num[2];
unsigned int key;
key = irq_lock();
cnt = ring_buf_get(&data->pool, buf, len);
irq_unlock(key);
if ((cnt == len) || ((flags & ENTROPY_BUSYWAIT) == 0U)) {
read = cnt;
} else {
buf += cnt;
len -= cnt;
/* Allowed to busy-wait. We should use a polling approach */
while (len) {
key = irq_lock();
src = TRNGStatusGet();
if (src & TRNG_NUMBER_READY) {
/*
* This function acknowledges the ready
* status
*/
num[1] = TRNGNumberGet(TRNG_HI_WORD);
num[0] = TRNGNumberGet(TRNG_LOW_WORD);
ring_buf_put(&data->pool, (uint8_t *)num,
sizeof(num));
}
/*
* If interrupts were enabled during busy wait, this
* would allow us to pick up anything that has been put
* in by the ISR as well.
*/
cnt = ring_buf_get(&data->pool, buf, len);
if (src & TRNG_FRO_SHUTDOWN) {
handle_shutdown_ovf();
}
irq_unlock(key);
if (cnt) {
buf += cnt;
len -= cnt;
} else {
k_busy_wait(US_PER_SAMPLE);
}
}
}
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)
{
const struct device *dev = (const 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(const 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(const 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(const 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);
/* Enable TRNG peripheral clocks */
PRCMPeripheralRunEnable(PRCM_PERIPH_TRNG);
/* Enabled the TRNG while in sleep mode to keep the entropy pool full. After
* the pool is full the TRNG will enter idle mode when random numbers are no
* longer being read. */
PRCMPeripheralSleepEnable(PRCM_PERIPH_TRNG);
PRCMPeripheralDeepSleepEnable(PRCM_PERIPH_TRNG);
/* Load PRCM settings */
PRCMLoadSet();
while (!PRCMLoadGet()) {
continue;
}
/* Peripherals should not be accessed until power domain is on. */
while (PRCMPowerDomainStatus(PRCM_DOMAIN_PERIPH) !=
PRCM_DOMAIN_POWER_ON) {
continue;
}
#endif
start_trng(data);
IRQ_CONNECT(DT_INST_IRQN(0),
DT_INST_IRQ(0, priority),
entropy_cc13xx_cc26xx_isr,
DEVICE_GET(entropy_cc13xx_cc26xx), 0);
irq_enable(DT_INST_IRQN(0));
return 0;
}
static const struct entropy_driver_api entropy_cc13xx_cc26xx_driver_api = {
.get_entropy = entropy_cc13xx_cc26xx_get_entropy,
.get_entropy_isr = entropy_cc13xx_cc26xx_get_entropy_isr,
};
static struct entropy_cc13xx_cc26xx_data entropy_cc13xx_cc26xx_data = {
.lock = Z_SEM_INITIALIZER(entropy_cc13xx_cc26xx_data.lock, 1, 1),
.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