Browse Source

drivers: gpio: split up driver for BD8LB600FS into a GPIO and MFD

Split up the driver for the low side switch BD8LB600FS into a GPIO
and MFD part.

Signed-off-by: Benedikt Schmidt <benedikt.schmidt@embedded-solutions.at>
pull/70276/head
Benedikt Schmidt 2 years ago committed by Alberto Escolar
parent
commit
31450fcb12
  1. 5
      drivers/gpio/Kconfig.bd8lb600fs
  2. 212
      drivers/gpio/gpio_bd8lb600fs.c
  3. 1
      drivers/mfd/CMakeLists.txt
  4. 1
      drivers/mfd/Kconfig
  5. 11
      drivers/mfd/Kconfig.bd8lb600fs
  6. 242
      drivers/mfd/mfd_bd8lb600fs.c
  7. 17
      dts/bindings/gpio/rohm,bd8lb600fs-gpio.yaml
  8. 31
      dts/bindings/mfd/rohm,bd8lb600fs.yaml
  9. 60
      include/zephyr/drivers/mfd/bd8lb600fs.h
  10. 3
      tests/drivers/build_all/gpio/adc_ads1145s0x_gpio.conf
  11. 13
      tests/drivers/build_all/gpio/app.overlay
  12. 1
      tests/drivers/build_all/gpio/prj.conf

5
drivers/gpio/Kconfig.bd8lb600fs

@ -1,13 +1,14 @@
# BD8LB600FS GPIO configuration options # BD8LB600FS GPIO configuration options
# Copyright (c) 2022 SILA Embedded Solutions GmbH # Copyright (c) 2022 SILA Embedded Solutions GmbH
# Copyright (c) 2024 SILA Embedded Solutions GmbH
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
menuconfig GPIO_BD8LB600FS menuconfig GPIO_BD8LB600FS
bool "BD8LB600FS SPI-based GPIO chip" bool "BD8LB600FS SPI-based GPIO chip"
default y default y
depends on DT_HAS_ROHM_BD8LB600FS_ENABLED depends on DT_HAS_ROHM_BD8LB600FS_GPIO_ENABLED
depends on SPI select MFD
help help
Enable driver for BD8LB600FS SPI-based GPIO chip. Enable driver for BD8LB600FS SPI-based GPIO chip.

212
drivers/gpio/gpio_bd8lb600fs.c

@ -1,111 +1,51 @@
/* /*
* Copyright (c) 2022 SILA Embedded Solutions GmbH * Copyright (c) 2022 SILA Embedded Solutions GmbH
* Copyright (c) 2024 SILA Embedded Solutions GmbH
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#define DT_DRV_COMPAT rohm_bd8lb600fs #define DT_DRV_COMPAT rohm_bd8lb600fs_gpio
#include <errno.h>
#include <zephyr/kernel.h> #include <zephyr/kernel.h>
#include <zephyr/device.h> #include <zephyr/device.h>
#include <zephyr/init.h> #include <zephyr/init.h>
#include <zephyr/drivers/gpio.h> #include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/gpio/gpio_utils.h> #include <zephyr/drivers/gpio/gpio_utils.h>
#include <zephyr/drivers/spi.h> #include <zephyr/drivers/mfd/bd8lb600fs.h>
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
#include <zephyr/sys/byteorder.h>
LOG_MODULE_REGISTER(gpio_bd8lb600fs, CONFIG_GPIO_LOG_LEVEL); LOG_MODULE_REGISTER(gpio_bd8lb600fs, CONFIG_GPIO_LOG_LEVEL);
#define OUTPUT_OFF_WITH_OPEN_LOAD_DETECTION 0b11 struct bd8lb600fs_gpio_config {
#define OUTPUT_ON 0b10
#define WAIT_TIME_RESET_ACTIVE_IN_US 1000
#define WAIT_TIME_RESET_INACTIVE_TO_CS_IN_US 10
struct bd8lb600fs_config {
/* gpio_driver_config needs to be first */ /* gpio_driver_config needs to be first */
struct gpio_driver_config common; struct gpio_driver_config common;
const struct device *parent_dev;
struct spi_dt_spec bus;
const struct gpio_dt_spec gpio_reset;
int gpios_count; int gpios_count;
}; };
struct bd8lb600fs_drv_data { struct bd8lb600fs_gpio_data {
/* gpio_driver_data needs to be first */ /* gpio_driver_data needs to be first */
struct gpio_driver_data data; struct gpio_driver_data data;
uint32_t state; /* each bit is one output channel, bit 0 = channel 1, ... */ /* each bit is one output channel, bit 0 = channel 1, ... */
uint32_t configured; /* each bit defines if the output channel is configured, see state */ uint32_t state;
/* each bit defines if the output channel is configured, see state */
uint32_t configured;
struct k_mutex lock; struct k_mutex lock;
int instance_count_actual;
int gpios_count_actual;
}; };
static int write_state(const struct device *dev, uint32_t state) static int bd8lb600fs_gpio_pin_configure(const struct device *dev, gpio_pin_t pin,
{ gpio_flags_t flags)
const struct bd8lb600fs_config *config = dev->config;
struct bd8lb600fs_drv_data *drv_data = dev->data;
LOG_DBG("%s: writing state 0x%08X to BD8LB600FS", dev->name, state);
uint16_t state_converted = 0;
uint8_t buffer_tx[8];
const struct spi_buf tx_buf = {
.buf = buffer_tx,
.len = drv_data->instance_count_actual * sizeof(state_converted),
};
const struct spi_buf_set tx = {
.buffers = &tx_buf,
.count = 1,
};
memset(buffer_tx, 0x00, sizeof(buffer_tx));
for (size_t j = 0; j < drv_data->instance_count_actual; ++j) {
int instance_position = (drv_data->instance_count_actual - j - 1) * 2;
state_converted = 0;
for (size_t i = 0; i < 8; ++i) {
if ((state & BIT(i + j*8)) == 0) {
state_converted |= OUTPUT_OFF_WITH_OPEN_LOAD_DETECTION << (i * 2);
} else {
state_converted |= OUTPUT_ON << (i * 2);
}
}
LOG_DBG("%s: configuration for instance %zu: %04X (position %i)",
dev->name,
j,
state_converted,
instance_position);
sys_put_be16(state_converted, buffer_tx + instance_position);
}
LOG_HEXDUMP_DBG(buffer_tx, ARRAY_SIZE(buffer_tx), "configuration written out");
int result = spi_write_dt(&config->bus, &tx);
if (result != 0) {
LOG_ERR("spi_write failed with error %i", result);
return result;
}
return 0;
}
static int bd8lb600fs_pin_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)
{ {
struct bd8lb600fs_drv_data *drv_data = dev->data; const struct bd8lb600fs_gpio_config *config = dev->config;
struct bd8lb600fs_gpio_data *data = dev->data;
/* cannot execute a bus operation in an ISR context */ /* cannot execute a bus operation in an ISR context */
if (k_is_in_isr()) { if (k_is_in_isr()) {
return -EWOULDBLOCK; return -EWOULDBLOCK;
} }
if (pin >= drv_data->gpios_count_actual) { if (pin >= config->gpios_count) {
LOG_ERR("invalid pin number %i", pin); LOG_ERR("invalid pin number %i", pin);
return -EINVAL; return -EINVAL;
} }
@ -140,158 +80,126 @@ static int bd8lb600fs_pin_configure(const struct device *dev, gpio_pin_t pin, gp
return -ENOTSUP; return -ENOTSUP;
} }
k_mutex_lock(&drv_data->lock, K_FOREVER); k_mutex_lock(&data->lock, K_FOREVER);
if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) { if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) {
WRITE_BIT(drv_data->state, pin, 0); WRITE_BIT(data->state, pin, 0);
} else if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) { } else if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) {
WRITE_BIT(drv_data->state, pin, 1); WRITE_BIT(data->state, pin, 1);
} }
WRITE_BIT(drv_data->configured, pin, 1); WRITE_BIT(data->configured, pin, 1);
int result = write_state(dev, drv_data->state); int result = mfd_bd8lb600fs_set_outputs(config->parent_dev, data->state);
k_mutex_unlock(&drv_data->lock); k_mutex_unlock(&data->lock);
return result; return result;
} }
static int bd8lb600fs_port_get_raw(const struct device *dev, uint32_t *value) static int bd8lb600fs_gpio_port_get_raw(const struct device *dev, uint32_t *value)
{ {
LOG_ERR("input pins are not available"); LOG_ERR("input pins are not available");
return -ENOTSUP; return -ENOTSUP;
} }
static int bd8lb600fs_port_set_masked_raw(const struct device *dev, uint32_t mask, uint32_t value) static int bd8lb600fs_gpio_port_set_masked_raw(const struct device *dev, uint32_t mask,
uint32_t value)
{ {
struct bd8lb600fs_drv_data *drv_data = dev->data; const struct bd8lb600fs_gpio_config *config = dev->config;
struct bd8lb600fs_gpio_data *data = dev->data;
/* cannot execute a bus operation in an ISR context */ /* cannot execute a bus operation in an ISR context */
if (k_is_in_isr()) { if (k_is_in_isr()) {
return -EWOULDBLOCK; return -EWOULDBLOCK;
} }
k_mutex_lock(&drv_data->lock, K_FOREVER); k_mutex_lock(&data->lock, K_FOREVER);
drv_data->state = (drv_data->state & ~mask) | (mask & value); data->state = (data->state & ~mask) | (mask & value);
int result = write_state(dev, drv_data->state); int result = mfd_bd8lb600fs_set_outputs(config->parent_dev, data->state);
k_mutex_unlock(&drv_data->lock); k_mutex_unlock(&data->lock);
return result; return result;
} }
static int bd8lb600fs_port_set_bits_raw(const struct device *dev, uint32_t mask) static int bd8lb600fs_gpio_port_set_bits_raw(const struct device *dev, uint32_t mask)
{ {
return bd8lb600fs_port_set_masked_raw(dev, mask, mask); return bd8lb600fs_gpio_port_set_masked_raw(dev, mask, mask);
} }
static int bd8lb600fs_port_clear_bits_raw(const struct device *dev, uint32_t mask) static int bd8lb600fs_gpio_port_clear_bits_raw(const struct device *dev, uint32_t mask)
{ {
return bd8lb600fs_port_set_masked_raw(dev, mask, 0); return bd8lb600fs_gpio_port_set_masked_raw(dev, mask, 0);
} }
static int bd8lb600fs_port_toggle_bits(const struct device *dev, uint32_t mask) static int bd8lb600fs_gpio_port_toggle_bits(const struct device *dev, uint32_t mask)
{ {
struct bd8lb600fs_drv_data *drv_data = dev->data; const struct bd8lb600fs_gpio_config *config = dev->config;
struct bd8lb600fs_gpio_data *data = dev->data;
/* cannot execute a bus operation in an ISR context */ /* cannot execute a bus operation in an ISR context */
if (k_is_in_isr()) { if (k_is_in_isr()) {
return -EWOULDBLOCK; return -EWOULDBLOCK;
} }
k_mutex_lock(&drv_data->lock, K_FOREVER); k_mutex_lock(&data->lock, K_FOREVER);
drv_data->state ^= mask; data->state ^= mask;
int result = write_state(dev, drv_data->state); int result = mfd_bd8lb600fs_set_outputs(config->parent_dev, data->state);
k_mutex_unlock(&drv_data->lock); k_mutex_unlock(&data->lock);
return result; return result;
} }
static const struct gpio_driver_api api_table = { static const struct gpio_driver_api api_table = {
.pin_configure = bd8lb600fs_pin_configure, .pin_configure = bd8lb600fs_gpio_pin_configure,
.port_get_raw = bd8lb600fs_port_get_raw, .port_get_raw = bd8lb600fs_gpio_port_get_raw,
.port_set_masked_raw = bd8lb600fs_port_set_masked_raw, .port_set_masked_raw = bd8lb600fs_gpio_port_set_masked_raw,
.port_set_bits_raw = bd8lb600fs_port_set_bits_raw, .port_set_bits_raw = bd8lb600fs_gpio_port_set_bits_raw,
.port_clear_bits_raw = bd8lb600fs_port_clear_bits_raw, .port_clear_bits_raw = bd8lb600fs_gpio_port_clear_bits_raw,
.port_toggle_bits = bd8lb600fs_port_toggle_bits, .port_toggle_bits = bd8lb600fs_gpio_port_toggle_bits,
}; };
static int bd8lb600fs_init(const struct device *dev) static int bd8lb600fs_gpio_init(const struct device *dev)
{ {
const struct bd8lb600fs_config *config = dev->config; const struct bd8lb600fs_gpio_config *config = dev->config;
struct bd8lb600fs_drv_data *drv_data = dev->data; struct bd8lb600fs_gpio_data *data = dev->data;
if (!spi_is_ready_dt(&config->bus)) {
LOG_ERR("SPI bus %s not ready", config->bus.bus->name);
return -ENODEV;
}
if (!gpio_is_ready_dt(&config->gpio_reset)) { if (!device_is_ready(config->parent_dev)) {
LOG_ERR("%s: reset GPIO is not ready", dev->name); LOG_ERR("MFD parent is not ready");
return -ENODEV; return -ENODEV;
} }
int result = k_mutex_init(&drv_data->lock); int result = k_mutex_init(&data->lock);
if (result != 0) { if (result != 0) {
LOG_ERR("unable to initialize mutex"); LOG_ERR("unable to initialize mutex");
return result; return result;
} }
drv_data->instance_count_actual = config->gpios_count / 8;
if (config->gpios_count % 8 != 0) {
LOG_ERR("%s: number of GPIOs %i is not a multiple of 8",
dev->name, config->gpios_count);
return -EINVAL;
}
if (drv_data->instance_count_actual > 4) {
LOG_ERR("%s: only a maximum of 4 devices are supported for the daisy chaining",
dev->name);
return -EINVAL;
}
drv_data->gpios_count_actual = drv_data->instance_count_actual * 8;
result = gpio_pin_configure_dt(&config->gpio_reset, GPIO_OUTPUT_ACTIVE);
if (result != 0) {
LOG_ERR("failed to initialize GPIO for reset");
return result;
}
k_busy_wait(WAIT_TIME_RESET_ACTIVE_IN_US);
gpio_pin_set_dt(&config->gpio_reset, 0);
k_busy_wait(WAIT_TIME_RESET_INACTIVE_TO_CS_IN_US);
return 0; return 0;
} }
#define BD8LB600FS_INIT(inst) \ #define BD8LB600FS_GPIO_INIT(inst) \
static const struct bd8lb600fs_config bd8lb600fs_##inst##_config = { \ static const struct bd8lb600fs_gpio_config bd8lb600fs_##inst##_config = { \
.common = \ .common = \
{ \ { \
.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(inst), \ .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(inst), \
}, \ }, \
.bus = SPI_DT_SPEC_INST_GET( \ .parent_dev = DEVICE_DT_GET(DT_INST_PARENT(inst)), \
inst, SPI_OP_MODE_MASTER | SPI_MODE_CPHA | SPI_WORD_SET(8), 0), \ .gpios_count = DT_INST_PROP(inst, ngpios), \
.gpio_reset = GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst), reset_gpios, 0), \
.gpios_count = DT_INST_PROP(inst, ngpios), \
}; \ }; \
\ \
static struct bd8lb600fs_drv_data bd8lb600fs_##inst##_drvdata = { \ static struct bd8lb600fs_gpio_data bd8lb600fs_##inst##_data = { \
.state = 0x00, \ .state = 0x00, \
.configured = 0x00, \ .configured = 0x00, \
}; \ }; \
\ \
/* This has to be initialized after the SPI peripheral. */ \ DEVICE_DT_INST_DEFINE(inst, bd8lb600fs_gpio_init, NULL, &bd8lb600fs_##inst##_data, \
DEVICE_DT_INST_DEFINE(inst, bd8lb600fs_init, NULL, &bd8lb600fs_##inst##_drvdata, \
&bd8lb600fs_##inst##_config, POST_KERNEL, \ &bd8lb600fs_##inst##_config, POST_KERNEL, \
CONFIG_GPIO_BD8LB600FS_INIT_PRIORITY, &api_table); CONFIG_GPIO_BD8LB600FS_INIT_PRIORITY, &api_table);
DT_INST_FOREACH_STATUS_OKAY(BD8LB600FS_INIT) DT_INST_FOREACH_STATUS_OKAY(BD8LB600FS_GPIO_INIT)

1
drivers/mfd/CMakeLists.txt

@ -10,3 +10,4 @@ zephyr_library_sources_ifdef(CONFIG_MFD_NPM6001 mfd_npm6001.c)
zephyr_library_sources_ifdef(CONFIG_MFD_AXP192 mfd_axp192.c) zephyr_library_sources_ifdef(CONFIG_MFD_AXP192 mfd_axp192.c)
zephyr_library_sources_ifdef(CONFIG_MFD_AD5592 mfd_ad5592.c) zephyr_library_sources_ifdef(CONFIG_MFD_AD5592 mfd_ad5592.c)
zephyr_library_sources_ifdef(CONFIG_NXP_LP_FLEXCOMM mfd_nxp_lp_flexcomm.c) zephyr_library_sources_ifdef(CONFIG_NXP_LP_FLEXCOMM mfd_nxp_lp_flexcomm.c)
zephyr_library_sources_ifdef(CONFIG_MFD_BD8LB600FS mfd_bd8lb600fs.c)

1
drivers/mfd/Kconfig

@ -20,6 +20,7 @@ config MFD_INIT_PRIORITY
source "drivers/mfd/Kconfig.ad5592" source "drivers/mfd/Kconfig.ad5592"
source "drivers/mfd/Kconfig.axp192" source "drivers/mfd/Kconfig.axp192"
source "drivers/mfd/Kconfig.bd8lb600fs"
source "drivers/mfd/Kconfig.max20335" source "drivers/mfd/Kconfig.max20335"
source "drivers/mfd/Kconfig.nct38xx" source "drivers/mfd/Kconfig.nct38xx"
source "drivers/mfd/Kconfig.npm1300" source "drivers/mfd/Kconfig.npm1300"

11
drivers/mfd/Kconfig.bd8lb600fs

@ -0,0 +1,11 @@
# Copyright (c) 2024 SILA Embedded Solutions GmbH
# SPDX -License-Identifier: Apache-2.0
config MFD_BD8LB600FS
bool "BD8LB600FS low side switch multi-function device driver"
default y
depends on DT_HAS_ROHM_BD8LB600FS_ENABLED
# using select SPI at this point introduces a cyclic dependency
depends on SPI
help
Enable the Rohm BD8LB600FS low side switch multi-function device driver

242
drivers/mfd/mfd_bd8lb600fs.c

@ -0,0 +1,242 @@
/*
* Copyright (c) 2024 SILA Embedded Solutions GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT rohm_bd8lb600fs
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/init.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/gpio/gpio_utils.h>
#include <zephyr/drivers/mfd/bd8lb600fs.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/byteorder.h>
LOG_MODULE_REGISTER(rohm_bd8lb600fs, CONFIG_MFD_LOG_LEVEL);
#define OUTPUT_OFF_WITH_OPEN_LOAD_DETECTION 0b11
#define OUTPUT_ON 0b10
#define WAIT_TIME_RESET_ACTIVE_IN_US 1000
#define WAIT_TIME_RESET_INACTIVE_TO_CS_IN_US 10
struct bd8lb600fs_config {
struct spi_dt_spec bus;
const struct gpio_dt_spec gpio_reset;
size_t instance_count;
};
struct bd8lb600fs_data {
/* each bit is one output channel, bit 0 = channel 1, ... */
uint32_t state;
/* each bit defines if an open load was detected, see state */
uint32_t old;
/* each bit defines if an over current or over temperature was detected, see state */
uint32_t ocp_or_tsd;
struct k_mutex lock;
};
static void bd8lb600fs_fill_tx_buffer(const struct device *dev, uint8_t *buffer, size_t buffer_size)
{
const struct bd8lb600fs_config *config = dev->config;
struct bd8lb600fs_data *data = dev->data;
uint16_t state_converted = 0;
LOG_DBG("%s: writing state 0x%08X to BD8LB600FS", dev->name, data->state);
memset(buffer, 0x00, buffer_size);
for (size_t j = 0; j < config->instance_count; ++j) {
int instance_position = (config->instance_count - j - 1) * 2;
state_converted = 0;
for (size_t i = 0; i < 8; ++i) {
if ((data->state & BIT(i + j * 8)) == 0) {
state_converted |= OUTPUT_OFF_WITH_OPEN_LOAD_DETECTION << (i * 2);
} else {
state_converted |= OUTPUT_ON << (i * 2);
}
}
LOG_DBG("%s: configuration for instance %zu: %04X (position %i)", dev->name, j,
state_converted, instance_position);
sys_put_be16(state_converted, buffer + instance_position);
}
}
static void bd8lb600fs_parse_rx_buffer(const struct device *dev, uint8_t *buffer)
{
const struct bd8lb600fs_config *config = dev->config;
struct bd8lb600fs_data *data = dev->data;
data->old = 0;
data->ocp_or_tsd = 0;
for (size_t j = 0; j < config->instance_count; ++j) {
int instance_position = (config->instance_count - j - 1) * 2;
uint16_t current = sys_get_be16(buffer + instance_position);
for (size_t i = 0; i < 8; ++i) {
if ((BIT(2 * i + 1) & current) != 0) {
WRITE_BIT(data->old, i + j * 8, 1);
}
if ((BIT(2 * i) & current) != 0) {
WRITE_BIT(data->ocp_or_tsd, i + j * 8, 1);
}
}
}
LOG_DBG("%s: received 0x%08X open load state from BD8LB600FS", dev->name, data->old);
LOG_DBG("%s: received 0x%08X OCP or TSD state from BD8LB600FS", dev->name,
data->ocp_or_tsd);
}
static int bd8lb600fs_transceive_state(const struct device *dev)
{
const struct bd8lb600fs_config *config = dev->config;
uint8_t buffer_tx[8];
const struct spi_buf tx_buf = {
.buf = buffer_tx,
.len = config->instance_count * sizeof(uint16_t),
};
const struct spi_buf_set tx = {
.buffers = &tx_buf,
.count = 1,
};
uint8_t buffer_rx[8];
const struct spi_buf rx_buf = {
.buf = buffer_rx,
.len = config->instance_count * sizeof(uint16_t),
};
const struct spi_buf_set rx = {
.buffers = &rx_buf,
.count = 1,
};
bd8lb600fs_fill_tx_buffer(dev, buffer_tx, ARRAY_SIZE(buffer_tx));
int result = spi_transceive_dt(&config->bus, &tx, &rx);
if (result != 0) {
LOG_ERR("spi_transceive failed with error %i", result);
return result;
}
bd8lb600fs_parse_rx_buffer(dev, buffer_rx);
return 0;
}
static int bd8lb600fs_write_state(const struct device *dev)
{
const struct bd8lb600fs_config *config = dev->config;
uint8_t buffer_tx[8];
const struct spi_buf tx_buf = {
.buf = buffer_tx,
.len = config->instance_count * sizeof(uint16_t),
};
const struct spi_buf_set tx = {
.buffers = &tx_buf,
.count = 1,
};
bd8lb600fs_fill_tx_buffer(dev, buffer_tx, ARRAY_SIZE(buffer_tx));
int result = spi_write_dt(&config->bus, &tx);
if (result != 0) {
LOG_ERR("spi_transceive failed with error %i", result);
return result;
}
return 0;
}
int mfd_bd8lb600fs_set_outputs(const struct device *dev, uint32_t values)
{
struct bd8lb600fs_data *data = dev->data;
int result;
k_mutex_lock(&data->lock, K_FOREVER);
data->state = values;
result = bd8lb600fs_write_state(dev);
k_mutex_unlock(&data->lock);
return result;
}
int mfd_bd8lb600fs_get_output_diagnostics(const struct device *dev, uint32_t *old,
uint32_t *ocp_or_tsd)
{
struct bd8lb600fs_data *data = dev->data;
int result;
k_mutex_lock(&data->lock, K_FOREVER);
result = bd8lb600fs_transceive_state(dev);
*old = data->old;
*ocp_or_tsd = data->ocp_or_tsd;
k_mutex_unlock(&data->lock);
return result;
}
static int bd8lb600fs_init(const struct device *dev)
{
const struct bd8lb600fs_config *config = dev->config;
struct bd8lb600fs_data *data = dev->data;
if (!spi_is_ready_dt(&config->bus)) {
LOG_ERR("SPI bus %s not ready", config->bus.bus->name);
return -ENODEV;
}
if (!gpio_is_ready_dt(&config->gpio_reset)) {
LOG_ERR("%s: reset GPIO is not ready", dev->name);
return -ENODEV;
}
int result = k_mutex_init(&data->lock);
if (result != 0) {
LOG_ERR("unable to initialize mutex");
return result;
}
result = gpio_pin_configure_dt(&config->gpio_reset, GPIO_OUTPUT_ACTIVE);
if (result != 0) {
LOG_ERR("failed to initialize GPIO for reset");
return result;
}
k_busy_wait(WAIT_TIME_RESET_ACTIVE_IN_US);
gpio_pin_set_dt(&config->gpio_reset, 0);
k_busy_wait(WAIT_TIME_RESET_INACTIVE_TO_CS_IN_US);
return 0;
}
#define BD8LB600FS_INIT(inst) \
static const struct bd8lb600fs_config bd8lb600fs_##inst##_config = { \
.bus = SPI_DT_SPEC_INST_GET( \
inst, SPI_OP_MODE_MASTER | SPI_MODE_CPHA | SPI_WORD_SET(8), 0), \
.gpio_reset = GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst), reset_gpios, 0), \
.instance_count = DT_INST_PROP(inst, instance_count), \
}; \
\
static struct bd8lb600fs_data bd8lb600fs_##inst##_data = { \
.state = 0x00, \
}; \
\
/* This has to be initialized after the SPI peripheral. */ \
DEVICE_DT_INST_DEFINE(inst, bd8lb600fs_init, NULL, &bd8lb600fs_##inst##_data, \
&bd8lb600fs_##inst##_config, POST_KERNEL, CONFIG_MFD_INIT_PRIORITY, \
NULL);
DT_INST_FOREACH_STATUS_OKAY(BD8LB600FS_INIT)

17
dts/bindings/gpio/rohm,bd8lb600fs.yaml → dts/bindings/gpio/rohm,bd8lb600fs-gpio.yaml

@ -1,5 +1,6 @@
# #
# Copyright (c) 2022 SILA Embedded Solutions GmbH # Copyright (c) 2022 SILA Embedded Solutions GmbH
# Copyright (c) 2024 SILA Embedded Solutions GmbH
# #
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
# #
@ -9,9 +10,11 @@ description: |
Multiple instances may be daisy chained, which can be configured Multiple instances may be daisy chained, which can be configured
via the number of supported GPIOs. via the number of supported GPIOs.
compatible: "rohm,bd8lb600fs" compatible: "rohm,bd8lb600fs-gpio"
include: [gpio-controller.yaml, spi-device.yaml] include: gpio-controller.yaml
on-bus: bd8lb600fs
properties: properties:
"#gpio-cells": "#gpio-cells":
@ -20,16 +23,16 @@ properties:
ngpios: ngpios:
type: int type: int
required: true required: true
enum:
- 8
- 16
- 24
- 32
description: | description: |
Number of pins for the expander. This must be a multiple of 8. Number of pins for the expander. This must be a multiple of 8.
The number of pins also defines how many devices are daisy chained. The number of pins also defines how many devices are daisy chained.
Set to 8 for one instance without daisy chaining. Set to 8 for one instance without daisy chaining.
reset-gpios:
type: phandle-array
required: true
description: GPIO for reset
gpio-cells: gpio-cells:
- pin - pin
- flags - flags

31
dts/bindings/mfd/rohm,bd8lb600fs.yaml

@ -0,0 +1,31 @@
#
# Copyright (c) 2024 SILA Embedded Solutions GmbH
#
# SPDX-License-Identifier: Apache-2.0
#
description: Rohm BD8LB600FS SPI Gpio Expander MFD
compatible: "rohm,bd8lb600fs"
include: spi-device.yaml
bus: bd8lb600fs
properties:
reset-gpios:
type: phandle-array
required: true
description: GPIO for reset
instance-count:
type: int
required: true
default: 1
enum:
- 1
- 2
- 3
- 4
description:
number of daisy chained instances, defaults to no daisy chaining

60
include/zephyr/drivers/mfd/bd8lb600fs.h

@ -0,0 +1,60 @@
/*
* Copyright (c) 2024 SILA Embedded Solutions GmbH
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_DRIVERS_MFD_BD8LB600FS_H_
#define ZEPHYR_INCLUDE_DRIVERS_MFD_BD8LB600FS_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <zephyr/device.h>
/**
* @defgroup mdf_interface_bd8lb600fs MFD BD8LB600FS interface
* @ingroup mfd_interfaces
* @{
*/
/**
* @brief set outputs
*
* @param[in] dev instance of BD8LB600FS MFD
* @param[in] values values for outputs, one bit per output
*
* @retval 0 if successful
*/
int mfd_bd8lb600fs_set_outputs(const struct device *dev, uint32_t values);
/**
* @brief get output diagnostics
*
* Fetch the current diagnostics from all instances, as multiple
* instances might be daisy chained together. Each bit in old
* and ocp_or_tsd corresponds to one output. A set bit means that the
* function is active, therefore either there is an open load, an over
* current or a too high temperature.
*
* OLD - open load
* OCP - over current protection
* TSD - thermal shutdown
*
* @param[in] dev instance of BD8LB600FS MFD
* @param[out] old open load values
* @param[out] ocp_or_tsd over current protection or thermal shutdown values
*
* @retval 0 if successful
*/
int mfd_bd8lb600fs_get_output_diagnostics(const struct device *dev, uint32_t *old,
uint32_t *ocp_or_tsd);
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_DRIVERS_MFD_BD8LB600FS_H_ */

3
tests/drivers/build_all/gpio/adc_ads1145s0x_gpio.conf

@ -1,3 +1,4 @@
CONFIG_ADC=y CONFIG_ADC=y
CONFIG_ADC_ADS114S0X_GPIO=y CONFIG_ADC_ADS114S0X_GPIO=y
CONFIG_ADC_INIT_PRIORITY=80 CONFIG_ADC_INIT_PRIORITY=81
CONFIG_GPIO_BD8LB600FS_INIT_PRIORITY=81

13
tests/drivers/build_all/gpio/app.overlay

@ -245,12 +245,19 @@
test_spi_bd8lb600fs: bd8lb600fs@2 { test_spi_bd8lb600fs: bd8lb600fs@2 {
compatible = "rohm,bd8lb600fs"; compatible = "rohm,bd8lb600fs";
status = "okay";
spi-max-frequency = <0>; spi-max-frequency = <0>;
reg = <0x02>; reg = <0x02>;
gpio-controller;
#gpio-cells = <2>;
ngpios = <8>;
reset-gpios = <&test_gpio 0 0>; reset-gpios = <&test_gpio 0 0>;
instance-count = <1>;
bd8lb600fs_gpio: bd8lb600fs_gpio {
compatible = "rohm,bd8lb600fs-gpio";
status = "okay";
gpio-controller;
#gpio-cells = <2>;
ngpios = <8>;
};
}; };
test_spi_ad5592: ad5592@3 { test_spi_ad5592: ad5592@3 {

1
tests/drivers/build_all/gpio/prj.conf

@ -4,3 +4,4 @@ CONFIG_TEST_USERSPACE=y
CONFIG_I2C=y CONFIG_I2C=y
CONFIG_GPIO_PCA95XX_INTERRUPT=y CONFIG_GPIO_PCA95XX_INTERRUPT=y
CONFIG_SPI=y CONFIG_SPI=y
CONFIG_GPIO_BD8LB600FS_INIT_PRIORITY=81

Loading…
Cancel
Save