Browse Source
MAX14917 is an eight high-side switch, specified to deliver up to 700mA (min) continuous current per channel. The high-side switches have on-resistance of 120mΩ (typ) at 25°C ambient temperature Signed-off-by: Robert Budai <robert.budai@analog.com>pull/87660/head
7 changed files with 502 additions and 0 deletions
@ -0,0 +1,20 @@ |
|||||||
|
# Copyright (c) 2025 Analog Devices Inc. |
||||||
|
# SPDX-License-Identifier: Apache-2.0 |
||||||
|
|
||||||
|
# MAX14917 GPIO configuration options |
||||||
|
|
||||||
|
menuconfig GPIO_MAX14917 |
||||||
|
bool "MAX14917 GPIO driver" |
||||||
|
default y |
||||||
|
select CRC |
||||||
|
depends on DT_HAS_ADI_MAX14917_GPIO_ENABLED && SPI |
||||||
|
help |
||||||
|
Enable MAX14917 octal industrial digital |
||||||
|
output with diagnostics |
||||||
|
|
||||||
|
config GPIO_MAX14917_INIT_PRIORITY |
||||||
|
int "Driver init priority" |
||||||
|
default 99 |
||||||
|
depends on GPIO_MAX14917 |
||||||
|
help |
||||||
|
Device driver initialization priority. |
@ -0,0 +1,358 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Analog Devices Inc. |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <zephyr/drivers/gpio.h> |
||||||
|
#include <zephyr/drivers/spi.h> |
||||||
|
#include <zephyr/kernel.h> |
||||||
|
#include <zephyr/sys/byteorder.h> |
||||||
|
#include <zephyr/sys/crc.h> |
||||||
|
|
||||||
|
#define LOG_LEVEL CONFIG_GPIO_LOG_LEVEL |
||||||
|
#include <zephyr/logging/log.h> |
||||||
|
|
||||||
|
LOG_MODULE_REGISTER(gpio_max14917); |
||||||
|
|
||||||
|
#include <zephyr/drivers/gpio/gpio_utils.h> |
||||||
|
|
||||||
|
#include "gpio_max14917.h" |
||||||
|
|
||||||
|
#define DT_DRV_COMPAT adi_max14917_gpio |
||||||
|
|
||||||
|
static int max14917_reg_trans_spi_diag(const struct device *dev) |
||||||
|
{ |
||||||
|
int ret = 0; |
||||||
|
uint8_t crc; |
||||||
|
|
||||||
|
uint8_t local_tx_buff[MAX14917_MAX_PKT_SIZE] = {0}; |
||||||
|
uint8_t local_rx_buff[MAX14917_MAX_PKT_SIZE] = {0}; |
||||||
|
|
||||||
|
struct max14917_data *data = dev->data; |
||||||
|
const struct max14917_config *config = dev->config; |
||||||
|
|
||||||
|
struct spi_buf tx_buf = { |
||||||
|
.buf = &local_tx_buff, |
||||||
|
.len = config->pkt_size, |
||||||
|
}; |
||||||
|
const struct spi_buf_set tx = {.buffers = &tx_buf, .count = 1}; |
||||||
|
|
||||||
|
struct spi_buf rx_buf = { |
||||||
|
.buf = &local_rx_buff, |
||||||
|
.len = config->pkt_size, |
||||||
|
}; |
||||||
|
const struct spi_buf_set rx = {.buffers = &rx_buf, .count = 1}; |
||||||
|
|
||||||
|
local_tx_buff[0] = data->gpios_ON; |
||||||
|
|
||||||
|
/* If CRC enabled calculate it */ |
||||||
|
if (config->crc_en) { |
||||||
|
crc = crc8(&local_tx_buff[0], 1, MAX14917_CRC_POLY, MAX14917_CRC_INI_VAL, false); |
||||||
|
local_tx_buff[1] = (crc & MAX14917_CRC_MASK); |
||||||
|
} |
||||||
|
|
||||||
|
/* Perform SPI transaction */ |
||||||
|
ret = spi_transceive_dt(&config->spi, &tx, &rx); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("SPI transfer failed"); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
/* If CRC enabled check it */ |
||||||
|
if (config->crc_en) { |
||||||
|
crc = crc8(&local_tx_buff[0], 1, MAX14917_CRC_POLY, MAX14917_CRC_INI_VAL, false); |
||||||
|
crc = (crc & MAX14917_CRC_MASK); |
||||||
|
if (crc != (local_rx_buff[1] & 0x1F)) { |
||||||
|
LOG_ERR("READ CRC ERR (%d)-(%d)\n", crc, (local_rx_buff[1] & 0x1F)); |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
/* Set error flags in device data */ |
||||||
|
data->comm_err = (local_rx_buff[1] & MAX14917_COMM_ERR); |
||||||
|
data->verr = (local_rx_buff[1] & MAX14917_VERR); |
||||||
|
data->therm_err = (local_rx_buff[1] & MAX14917_THERM_ERR); |
||||||
|
} |
||||||
|
|
||||||
|
data->gpios_fault = local_rx_buff[0]; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int max14917_fault_check(const struct device *dev) |
||||||
|
{ |
||||||
|
int ret; |
||||||
|
const struct max14917_data *data = dev->data; |
||||||
|
const struct max14917_config *config = dev->config; |
||||||
|
|
||||||
|
if (gpio_pin_get_dt(&config->fault_gpio)) { |
||||||
|
LOG_DBG("FAULT GPIO is high"); |
||||||
|
} |
||||||
|
|
||||||
|
/* Update error flags */ |
||||||
|
ret = max14917_reg_trans_spi_diag(dev); |
||||||
|
if (ret) { |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
if (data->comm_err) { |
||||||
|
LOG_DBG("COMMERR flag is active"); |
||||||
|
} |
||||||
|
if (data->verr) { |
||||||
|
LOG_DBG("VERR flag is active"); |
||||||
|
} |
||||||
|
if (data->therm_err) { |
||||||
|
LOG_DBG("THERMERR flag is active"); |
||||||
|
} |
||||||
|
|
||||||
|
for (int i = 0; i < MAX14917_CHANNELS; i++) { |
||||||
|
if (data->gpios_fault & BIT(i)) { |
||||||
|
LOG_DBG("Channel %d has a fault", i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int gpio_max14917_init(const struct device *dev) |
||||||
|
{ |
||||||
|
struct max14917_data *data = dev->data; |
||||||
|
const struct max14917_config *config = dev->config; |
||||||
|
int err = 0; |
||||||
|
|
||||||
|
LOG_DBG(" --- GPIO max14917 init IN ---"); |
||||||
|
|
||||||
|
if (!spi_is_ready_dt(&config->spi)) { |
||||||
|
LOG_ERR("SPI bus is not ready\n"); |
||||||
|
return -ENODEV; |
||||||
|
} |
||||||
|
|
||||||
|
/* Output GPIOS */ |
||||||
|
/* setup EN gpio - normal low */ |
||||||
|
if (!gpio_is_ready_dt(&config->en_gpio)) { |
||||||
|
LOG_ERR("EN GPIO device not ready"); |
||||||
|
return -ENODEV; |
||||||
|
} |
||||||
|
|
||||||
|
err = gpio_pin_configure_dt(&config->en_gpio, GPIO_OUTPUT); |
||||||
|
if (err < 0) { |
||||||
|
LOG_ERR("Failed to configure EN GPIO"); |
||||||
|
return err; |
||||||
|
} |
||||||
|
|
||||||
|
/* setup SYNC gpio - normal low */ |
||||||
|
if (!gpio_is_ready_dt(&config->sync_gpio)) { |
||||||
|
LOG_ERR("SYNC GPIO device not ready"); |
||||||
|
return -ENODEV; |
||||||
|
} |
||||||
|
|
||||||
|
err = gpio_pin_configure_dt(&config->sync_gpio, GPIO_OUTPUT); |
||||||
|
if (err < 0) { |
||||||
|
LOG_ERR("Failed to configure SYNC GPIO"); |
||||||
|
return err; |
||||||
|
} |
||||||
|
/* setup CRCEN gpio - normal low */ |
||||||
|
if (!gpio_is_ready_dt(&config->crcen_gpio)) { |
||||||
|
LOG_ERR("CRCEN GPIO device not ready"); |
||||||
|
return -ENODEV; |
||||||
|
} |
||||||
|
|
||||||
|
err = gpio_pin_configure_dt(&config->crcen_gpio, GPIO_OUTPUT); |
||||||
|
if (err < 0) { |
||||||
|
LOG_ERR("Failed to configure CRCEN GPIO"); |
||||||
|
return err; |
||||||
|
} |
||||||
|
/* Input GPIOS */ |
||||||
|
/* setup VDDOK gpio - normal low */ |
||||||
|
if (!gpio_is_ready_dt(&config->vddok_gpio)) { |
||||||
|
LOG_ERR("VDDOK GPIO device not ready"); |
||||||
|
return -ENODEV; |
||||||
|
} |
||||||
|
|
||||||
|
err = gpio_pin_configure_dt(&config->vddok_gpio, GPIO_INPUT); |
||||||
|
if (err < 0) { |
||||||
|
LOG_ERR("Failed to configure VDDOK GPIO"); |
||||||
|
return err; |
||||||
|
} |
||||||
|
/* setup READY gpio - normal low */ |
||||||
|
if (!gpio_is_ready_dt(&config->ready_gpio)) { |
||||||
|
LOG_ERR("VDDOK READY device not ready"); |
||||||
|
return -ENODEV; |
||||||
|
} |
||||||
|
|
||||||
|
err = gpio_pin_configure_dt(&config->ready_gpio, GPIO_INPUT); |
||||||
|
if (err < 0) { |
||||||
|
LOG_ERR("Failed to configure READY GPIO"); |
||||||
|
return err; |
||||||
|
} |
||||||
|
/* setup COMERR gpio - normal low */ |
||||||
|
if (!gpio_is_ready_dt(&config->comerr_gpio)) { |
||||||
|
LOG_ERR("COMERR GPIO device not ready"); |
||||||
|
return -ENODEV; |
||||||
|
} |
||||||
|
|
||||||
|
err = gpio_pin_configure_dt(&config->comerr_gpio, GPIO_INPUT); |
||||||
|
if (err < 0) { |
||||||
|
LOG_ERR("Failed to configure COMERR GPIO"); |
||||||
|
return err; |
||||||
|
} |
||||||
|
/* setup FAULT gpio - normal low */ |
||||||
|
if (!gpio_is_ready_dt(&config->fault_gpio)) { |
||||||
|
LOG_ERR("FAULT GPIO device not ready"); |
||||||
|
return -ENODEV; |
||||||
|
} |
||||||
|
|
||||||
|
err = gpio_pin_configure_dt(&config->fault_gpio, GPIO_INPUT); |
||||||
|
if (err < 0) { |
||||||
|
LOG_ERR("Failed to configure FAULT GPIO"); |
||||||
|
return err; |
||||||
|
} |
||||||
|
|
||||||
|
err = gpio_pin_set_dt(&config->en_gpio, 1); |
||||||
|
if (err) { |
||||||
|
return err; |
||||||
|
} |
||||||
|
err = gpio_pin_set_dt(&config->sync_gpio, 1); |
||||||
|
if (err) { |
||||||
|
return err; |
||||||
|
} |
||||||
|
|
||||||
|
if (config->crc_en) { |
||||||
|
err = gpio_pin_set_dt(&config->crcen_gpio, 1); |
||||||
|
} else { |
||||||
|
err = gpio_pin_set_dt(&config->crcen_gpio, 0); |
||||||
|
} |
||||||
|
|
||||||
|
if (err) { |
||||||
|
return err; |
||||||
|
} |
||||||
|
|
||||||
|
/* Initialize satus and fault flags to 0 */ |
||||||
|
data->gpios_ON = 0; |
||||||
|
data->gpios_fault = 0; |
||||||
|
|
||||||
|
err = max14917_fault_check(dev); |
||||||
|
|
||||||
|
return err; |
||||||
|
} |
||||||
|
|
||||||
|
static int gpio_max14917_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) |
||||||
|
{ |
||||||
|
int err = 0; |
||||||
|
|
||||||
|
if ((flags & (GPIO_INPUT | GPIO_OUTPUT)) == GPIO_DISCONNECTED) { |
||||||
|
return -ENOTSUP; |
||||||
|
} |
||||||
|
|
||||||
|
if ((flags & GPIO_SINGLE_ENDED) != 0) { |
||||||
|
return -ENOTSUP; |
||||||
|
} |
||||||
|
|
||||||
|
if ((flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) != 0) { |
||||||
|
return -ENOTSUP; |
||||||
|
} |
||||||
|
|
||||||
|
if (flags & GPIO_INT_ENABLE) { |
||||||
|
return -ENOTSUP; |
||||||
|
} |
||||||
|
|
||||||
|
switch (flags & GPIO_DIR_MASK) { |
||||||
|
case GPIO_OUTPUT: |
||||||
|
break; |
||||||
|
case GPIO_INPUT: |
||||||
|
default: |
||||||
|
LOG_ERR("NOT SUPPORTED OPTION!"); |
||||||
|
return -ENOTSUP; |
||||||
|
} |
||||||
|
|
||||||
|
return err; |
||||||
|
} |
||||||
|
|
||||||
|
static int gpio_max14917_port_get_raw(const struct device *dev, gpio_port_value_t *value) |
||||||
|
{ |
||||||
|
int ret; |
||||||
|
const struct max14917_data *data = dev->data; |
||||||
|
|
||||||
|
ret = max14917_fault_check(dev); |
||||||
|
if (ret) { |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
*value = data->gpios_ON; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int gpio_max14917_port_set_bits_raw(const struct device *dev, gpio_port_pins_t pins) |
||||||
|
{ |
||||||
|
int ret; |
||||||
|
struct max14917_data *data = dev->data; |
||||||
|
|
||||||
|
ret = max14917_fault_check(dev); |
||||||
|
if (ret) { |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
data->gpios_ON = data->gpios_ON | pins; |
||||||
|
|
||||||
|
return max14917_reg_trans_spi_diag(dev); |
||||||
|
} |
||||||
|
|
||||||
|
static int gpio_max14917_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t pins) |
||||||
|
{ |
||||||
|
int ret; |
||||||
|
struct max14917_data *data = dev->data; |
||||||
|
|
||||||
|
ret = max14917_fault_check(dev); |
||||||
|
if (ret) { |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
data->gpios_ON = data->gpios_ON & ~pins; |
||||||
|
|
||||||
|
return max14917_reg_trans_spi_diag(dev); |
||||||
|
} |
||||||
|
|
||||||
|
static int gpio_max14917_port_toggle_bits(const struct device *dev, gpio_port_pins_t pins) |
||||||
|
{ |
||||||
|
int ret; |
||||||
|
struct max14917_data *data = dev->data; |
||||||
|
|
||||||
|
ret = max14917_fault_check(dev); |
||||||
|
if (ret) { |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
data->gpios_ON ^= pins; |
||||||
|
|
||||||
|
return max14917_reg_trans_spi_diag(dev); |
||||||
|
} |
||||||
|
|
||||||
|
static DEVICE_API(gpio, gpio_max14917_api) = { |
||||||
|
.pin_configure = gpio_max14917_config, |
||||||
|
.port_get_raw = gpio_max14917_port_get_raw, |
||||||
|
.port_set_bits_raw = gpio_max14917_port_set_bits_raw, |
||||||
|
.port_clear_bits_raw = gpio_max14917_port_clear_bits_raw, |
||||||
|
.port_toggle_bits = gpio_max14917_port_toggle_bits, |
||||||
|
}; |
||||||
|
|
||||||
|
#define GPIO_MAX14917_DEVICE(id) \ |
||||||
|
static const struct max14917_config max14917_##id##_cfg = { \ |
||||||
|
.spi = SPI_DT_SPEC_INST_GET(id, SPI_OP_MODE_MASTER | SPI_WORD_SET(8U), 0U), \ |
||||||
|
.vddok_gpio = GPIO_DT_SPEC_INST_GET(id, vddok_gpios), \ |
||||||
|
.ready_gpio = GPIO_DT_SPEC_INST_GET(id, ready_gpios), \ |
||||||
|
.comerr_gpio = GPIO_DT_SPEC_INST_GET(id, comerr_gpios), \ |
||||||
|
.fault_gpio = GPIO_DT_SPEC_INST_GET(id, fault_gpios), \ |
||||||
|
.en_gpio = GPIO_DT_SPEC_INST_GET(id, en_gpios), \ |
||||||
|
.sync_gpio = GPIO_DT_SPEC_INST_GET(id, sync_gpios), \ |
||||||
|
.crcen_gpio = GPIO_DT_SPEC_INST_GET(id, crcen_gpios), \ |
||||||
|
.crc_en = DT_INST_PROP(id, crc_en), \ |
||||||
|
.pkt_size = (DT_INST_PROP(id, crc_en) & 0x1) ? 2 : 1, \ |
||||||
|
}; \ |
||||||
|
\ |
||||||
|
static struct max14917_data max14917_##id##_data; \ |
||||||
|
\ |
||||||
|
DEVICE_DT_INST_DEFINE(id, &gpio_max14917_init, NULL, &max14917_##id##_data, \ |
||||||
|
&max14917_##id##_cfg, POST_KERNEL, \ |
||||||
|
CONFIG_GPIO_MAX14917_INIT_PRIORITY, &gpio_max14917_api); |
||||||
|
|
||||||
|
DT_INST_FOREACH_STATUS_OKAY(GPIO_MAX14917_DEVICE) |
@ -0,0 +1,46 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Analog Devices Inc. |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef ZEPHYR_DRIVERS_GPIO_GPIO_MAX14917_H_ |
||||||
|
#define ZEPHYR_DRIVERS_GPIO_GPIO_MAX14917_H_ |
||||||
|
|
||||||
|
#define MAX14917_CHANNELS 8 |
||||||
|
|
||||||
|
#define MAX14917_MAX_PKT_SIZE 2 |
||||||
|
|
||||||
|
#define MAX14917_COMM_ERR BIT(7) |
||||||
|
#define MAX14917_VERR BIT(6) |
||||||
|
#define MAX14917_THERM_ERR BIT(5) |
||||||
|
|
||||||
|
#define MAX14917_CRC_POLY 0x15 |
||||||
|
#define MAX14917_CRC_INI_VAL 0x1F |
||||||
|
#define MAX14917_CRC_EXTRA_BYTE 0x00 |
||||||
|
#define MAX14917_CRC_MASK 0x1F |
||||||
|
|
||||||
|
struct max14917_config { |
||||||
|
struct spi_dt_spec spi; |
||||||
|
/* Input gpios */ |
||||||
|
struct gpio_dt_spec vddok_gpio; |
||||||
|
struct gpio_dt_spec ready_gpio; |
||||||
|
struct gpio_dt_spec comerr_gpio; |
||||||
|
struct gpio_dt_spec fault_gpio; |
||||||
|
/* Output gpios */ |
||||||
|
struct gpio_dt_spec en_gpio; |
||||||
|
struct gpio_dt_spec sync_gpio; |
||||||
|
struct gpio_dt_spec crcen_gpio; |
||||||
|
bool crc_en; |
||||||
|
uint8_t pkt_size; |
||||||
|
}; |
||||||
|
|
||||||
|
struct max14917_data { |
||||||
|
uint8_t gpios_ON; /* GPIO states */ |
||||||
|
uint8_t gpios_fault; |
||||||
|
bool comm_err; |
||||||
|
bool verr; |
||||||
|
bool therm_err; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,59 @@ |
|||||||
|
# Copyright (c) 2025 Analog Devices Inc. |
||||||
|
# SPDX-License-Identifier: Apache-2.0 |
||||||
|
|
||||||
|
description: ADI MAX14917 is octal industrial output |
||||||
|
|
||||||
|
compatible: "adi,max14917-gpio" |
||||||
|
|
||||||
|
properties: |
||||||
|
"#gpio-cells": |
||||||
|
const: 2 |
||||||
|
ngpios: |
||||||
|
type: int |
||||||
|
required: true |
||||||
|
const: 8 |
||||||
|
description: Number of gpios supported |
||||||
|
vddok-gpios: |
||||||
|
description: | |
||||||
|
High-Side Open-Drain Output. VDDOK is passive low when the internal |
||||||
|
logic supply is higher than the UVLO threshold, indicating that the |
||||||
|
registers have adequate supply voltage. |
||||||
|
type: phandle-array |
||||||
|
ready-gpios: |
||||||
|
description: | |
||||||
|
Ready pin indicates when the device is ready to be used. |
||||||
|
type: phandle-array |
||||||
|
comerr-gpios: |
||||||
|
description: | |
||||||
|
Communication Error pin indicates when there is a communication error |
||||||
|
on the SPI bus. |
||||||
|
type: phandle-array |
||||||
|
fault-gpios: |
||||||
|
description: | |
||||||
|
Fault pin indicates cases of overload or undervoltage conditions |
||||||
|
type: phandle-array |
||||||
|
en-gpios: |
||||||
|
description: | |
||||||
|
Enable Pin. Drive the EN pin high to enable the outputs. |
||||||
|
Drive EN low to disable/three-state all outputs. |
||||||
|
type: phandle-array |
||||||
|
sync-gpios: |
||||||
|
description: | |
||||||
|
All eight output switches are updated simultaneously on the rising edge |
||||||
|
of SYNCH, as determined by the content of the SPI command |
||||||
|
type: phandle-array |
||||||
|
crcen-gpios: |
||||||
|
description: | |
||||||
|
Drive CRCEN high to enable CRC generation and error detection on the |
||||||
|
serial data. |
||||||
|
type: phandle-array |
||||||
|
crc-en: |
||||||
|
description: | |
||||||
|
Notify driver if crc pin is enabled. |
||||||
|
type: boolean |
||||||
|
|
||||||
|
gpio-cells: |
||||||
|
- pin |
||||||
|
- flags |
||||||
|
|
||||||
|
include: [gpio-controller.yaml, spi-device.yaml] |
Loading…
Reference in new issue