zephyr/drivers/gpio/gpio_hogs.c

126 lines
3.5 KiB
C
Raw Normal View History

/*
* Copyright (c) 2022-2023 Vestas Wind Systems A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/devicetree.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/init.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(gpio_hogs, CONFIG_GPIO_LOG_LEVEL);
struct gpio_hog_dt_spec {
gpio_pin_t pin;
gpio_flags_t flags;
};
struct gpio_hogs {
const struct device *port;
const struct gpio_hog_dt_spec *specs;
size_t num_specs;
};
/* Static initializer for a struct gpio_hog_dt_spec */
#define GPIO_HOG_DT_SPEC_GET_BY_IDX(node_id, idx) \
{ \
.pin = DT_GPIO_HOG_PIN_BY_IDX(node_id, idx), \
.flags = DT_GPIO_HOG_FLAGS_BY_IDX(node_id, idx) | \
COND_CODE_1(DT_PROP(node_id, input), (GPIO_INPUT), \
(COND_CODE_1(DT_PROP(node_id, output_low), \
(GPIO_OUTPUT_INACTIVE), \
(COND_CODE_1(DT_PROP(node_id, output_high), \
(GPIO_OUTPUT_ACTIVE), (0)))))), \
}
/* Expands to 1 if node_id is a GPIO controller, 0 otherwise */
#define GPIO_HOGS_NODE_IS_GPIO_CTLR(node_id) \
DT_PROP_OR(node_id, gpio_controller, 0)
/* Expands to to 1 if node_id is a GPIO hog, empty otherwise */
#define GPIO_HOGS_NODE_IS_GPIO_HOG(node_id) \
IF_ENABLED(DT_PROP_OR(node_id, gpio_hog, 0), 1)
/* Expands to 1 if GPIO controller node_id has GPIO hog children, 0 otherwise */
#define GPIO_HOGS_GPIO_CTLR_HAS_HOGS(node_id) \
COND_CODE_0( \
IS_EMPTY(DT_FOREACH_CHILD_STATUS_OKAY(node_id, \
GPIO_HOGS_NODE_IS_GPIO_HOG)), \
(1), (0))
/* Called for GPIO hog indexes */
#define GPIO_HOGS_INIT_GPIO_HOG_BY_IDX(idx, node_id) \
GPIO_HOG_DT_SPEC_GET_BY_IDX(node_id, idx)
/* Called for GPIO hog dts nodes */
#define GPIO_HOGS_INIT_GPIO_HOGS(node_id) \
LISTIFY(DT_NUM_GPIO_HOGS(node_id), \
GPIO_HOGS_INIT_GPIO_HOG_BY_IDX, (,), node_id),
/* Called for GPIO controller dts node children */
#define GPIO_HOGS_COND_INIT_GPIO_HOGS(node_id) \
COND_CODE_0(IS_EMPTY(GPIO_HOGS_NODE_IS_GPIO_HOG(node_id)), \
(GPIO_HOGS_INIT_GPIO_HOGS(node_id)), ())
/* Called for each GPIO controller dts node which has GPIO hog children */
#define GPIO_HOGS_INIT_GPIO_CTLR(node_id) \
{ \
.port = DEVICE_DT_GET(node_id), \
.specs = (const struct gpio_hog_dt_spec []) { \
DT_FOREACH_CHILD_STATUS_OKAY(node_id, \
GPIO_HOGS_COND_INIT_GPIO_HOGS) \
}, \
.num_specs = \
DT_FOREACH_CHILD_STATUS_OKAY_SEP(node_id, \
DT_NUM_GPIO_HOGS, (+)), \
},
/* Called for each GPIO controller dts node */
#define GPIO_HOGS_COND_INIT_GPIO_CTLR(node_id) \
IF_ENABLED(GPIO_HOGS_GPIO_CTLR_HAS_HOGS(node_id), \
(GPIO_HOGS_INIT_GPIO_CTLR(node_id)))
/* Called for each dts node */
#define GPIO_HOGS_COND_INIT(node_id) \
IF_ENABLED(GPIO_HOGS_NODE_IS_GPIO_CTLR(node_id), \
(GPIO_HOGS_COND_INIT_GPIO_CTLR(node_id)))
static const struct gpio_hogs gpio_hogs[] = {
DT_FOREACH_STATUS_OKAY_NODE(GPIO_HOGS_COND_INIT)
};
init: remove the need for a dummy device pointer in SYS_INIT functions The init infrastructure, found in `init.h`, is currently used by: - `SYS_INIT`: to call functions before `main` - `DEVICE_*`: to initialize devices They are all sorted according to an initialization level + a priority. `SYS_INIT` calls are really orthogonal to devices, however, the required function signature requires a `const struct device *dev` as a first argument. The only reason for that is because the same init machinery is used by devices, so we have something like: ```c struct init_entry { int (*init)(const struct device *dev); /* only set by DEVICE_*, otherwise NULL */ const struct device *dev; } ``` As a result, we end up with such weird/ugly pattern: ```c static int my_init(const struct device *dev) { /* always NULL! add ARG_UNUSED to avoid compiler warning */ ARG_UNUSED(dev); ... } ``` This is really a result of poor internals isolation. This patch proposes a to make init entries more flexible so that they can accept sytem initialization calls like this: ```c static int my_init(void) { ... } ``` This is achieved using a union: ```c union init_function { /* for SYS_INIT, used when init_entry.dev == NULL */ int (*sys)(void); /* for DEVICE*, used when init_entry.dev != NULL */ int (*dev)(const struct device *dev); }; struct init_entry { /* stores init function (either for SYS_INIT or DEVICE*) union init_function init_fn; /* stores device pointer for DEVICE*, NULL for SYS_INIT. Allows * to know which union entry to call. */ const struct device *dev; } ``` This solution **does not increase ROM usage**, and allows to offer clean public APIs for both SYS_INIT and DEVICE*. Note that however, init machinery keeps a coupling with devices. **NOTE**: This is a breaking change! All `SYS_INIT` functions will need to be converted to the new signature. See the script offered in the following commit. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no> init: convert SYS_INIT functions to the new signature Conversion scripted using scripts/utils/migrate_sys_init.py. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no> manifest: update projects for SYS_INIT changes Update modules with updated SYS_INIT calls: - hal_ti - lvgl - sof - TraceRecorderSource Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no> tests: devicetree: devices: adjust test Adjust test according to the recently introduced SYS_INIT infrastructure. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no> tests: kernel: threads: adjust SYS_INIT call Adjust to the new signature: int (*init_fn)(void); Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2022-10-19 15:33:44 +08:00
static int gpio_hogs_init(void)
{
const struct gpio_hogs *hogs;
const struct gpio_hog_dt_spec *spec;
int err;
int i;
int j;
for (i = 0; i < ARRAY_SIZE(gpio_hogs); i++) {
hogs = &gpio_hogs[i];
if (!device_is_ready(hogs->port)) {
LOG_ERR("GPIO port %s not ready", hogs->port->name);
return -ENODEV;
}
for (j = 0; j < hogs->num_specs; j++) {
spec = &hogs->specs[j];
err = gpio_pin_configure(hogs->port, spec->pin, spec->flags);
if (err < 0) {
LOG_ERR("failed to configure GPIO hog for port %s pin %u (err %d)",
hogs->port->name, spec->pin, err);
return err;
}
}
}
return 0;
}
SYS_INIT(gpio_hogs_init, POST_KERNEL, CONFIG_GPIO_HOGS_INIT_PRIORITY);