Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

666 lines
20 KiB

/*
* Copyright (c) 2024 Analog Devices Inc.
* Copyright (c) 2024 Baylibre SAS
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT adi_max22190_gpio
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/byteorder.h>
#define LOG_LEVEL CONFIG_GPIO_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(gpio_max22190);
#include <zephyr/drivers/gpio/gpio_utils.h>
#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 0
#warning "GPIO MAX22190 driver enabled without any devices"
#endif
#define MAX22190_ENABLE 1
#define MAX22190_DISABLE 0
#define MAX22190_READ 0
#define MAX22190_WRITE 1
#define MAX22190_MAX_PKT_SIZE 3
#define MAX22190_CHANNELS 8
#define MAX22190_FAULT2_ENABLES 5
#define MAX22190_WB_REG 0x0
#define MAX22190_DI_REG 0x2
#define MAX22190_FAULT1_REG 0x4
#define MAX22190_FILTER_IN_REG(x) (0x6 + (2 * (x)))
#define MAX22190_CFG_REG 0x18
#define MAX22190_IN_EN_REG 0x1A
#define MAX22190_FAULT2_REG 0x1C
#define MAX22190_FAULT2_EN_REG 0x1E
#define MAX22190_GPO_REG 0x22
#define MAX22190_FAULT1_EN_REG 0x24
#define MAX22190_NOP_REG 0x26
#define MAX22190_CH_STATE_MASK(x) BIT(x)
#define MAX22190_DELAY_MASK GENMASK(2, 0)
#define MAX22190_FBP_MASK BIT(3)
#define MAX22190_WBE_MASK BIT(4)
#define MAX22190_RW_MASK BIT(7)
#define MAX22190_ADDR_MASK GENMASK(6, 0)
#define MAX22190_ALARM_MASK GENMASK(4, 3)
#define MAX22190_POR_MASK BIT(6)
#define MAX22190_FAULT_MASK(x) BIT(x)
#define MAX22190_FAULT2_WBE_MASK BIT(4)
#define MAX22190_FAULT2_EN_MASK GENMASK(5, 0)
#define MAX22190_CFG_REFDI_MASK BIT(0)
#define MAX22190_CFG_CLRF_MASK BIT(3)
#define MAX22190_CFG_24VF_MASK BIT(4)
#define PRINT_ERR_BIT(bit1, bit2) \
if (bit1 & bit2) { \
LOG_ERR("[%s] %d", #bit1, bit1); \
}
#define MAX22190_CLEAN_POR(dev) \
max22190_reg_update(dev, MAX22190_FAULT1_REG, MAX22190_POR_MASK, \
FIELD_PREP(MAX22190_POR_MASK, 0))
enum max22190_ch_state {
MAX22190_CH_OFF,
MAX22190_CH_ON
};
enum max22190_ch_wb_state {
MAX22190_CH_NO_WB_BREAK,
MAX22190_CH_WB_COND_DET
};
enum max22190_mode {
MAX22190_MODE_0,
MAX22190_MODE_1,
MAX22190_MODE_2,
MAX22190_MODE_3,
};
union max22190_fault1 {
uint8_t reg_raw;
struct {
uint8_t max22190_WBG: 1; /* BIT 0 */
uint8_t max22190_24VM: 1;
uint8_t max22190_24VL: 1;
uint8_t max22190_ALRMT1: 1;
uint8_t max22190_ALRMT2: 1;
uint8_t max22190_FAULT2: 1;
uint8_t max22190_POR: 1;
uint8_t max22190_CRC: 1; /* BIT 7 */
} reg_bits;
};
union max22190_fault1_en {
uint8_t reg_raw;
struct {
uint8_t max22190_WBGE: 1; /* BIT 0 */
uint8_t max22190_24VME: 1;
uint8_t max22190_24VLE: 1;
uint8_t max22190_ALRMT1E: 1;
uint8_t max22190_ALRMT2E: 1;
uint8_t max22190_FAULT2E: 1;
uint8_t max22190_PORE: 1;
uint8_t max22190_CRCE: 1; /* BIT 7 */
} reg_bits;
};
union max22190_fault2 {
uint8_t reg_raw;
struct {
uint8_t max22190_RFWBS: 1; /* BIT 0 */
uint8_t max22190_RFWBO: 1;
uint8_t max22190_RFDIS: 1;
uint8_t max22190_RFDIO: 1;
uint8_t max22190_OTSHDN: 1;
uint8_t max22190_FAULT8CK: 1;
uint8_t max22190_DUMMY: 2; /* BIT 7 */
} reg_bits;
};
union max22190_fault2_en {
uint8_t reg_raw;
struct {
uint8_t max22190_RFWBSE: 1; /* BIT 0 */
uint8_t max22190_RFWBOE: 1;
uint8_t max22190_RFDISE: 1;
uint8_t max22190_RFDIOE: 1;
uint8_t max22190_OTSHDNE: 1;
uint8_t max22190_FAULT8CKE: 1;
uint8_t max22190_DUMMY: 2; /* BIT 7 */
} reg_bits;
};
union max22190_cfg {
uint8_t reg_raw;
struct {
uint8_t max22190_DUMMY1: 3; /* BIT 0 */
uint8_t max22190_24VF: 1;
uint8_t max22190_CLRF: 1;
uint8_t max22190_DUMMY2: 2;
uint8_t max22190_REFDI_SH_EN: 1; /* BIT 7 */
} reg_bits;
};
union max22190_filter {
uint8_t reg_raw;
struct {
uint8_t max22190_DELAY: 3; /* BIT 0 */
uint8_t max22190_FBP: 1;
uint8_t max22190_WBE: 1;
uint8_t max22190_DUMMY: 2; /* BIT 7 */
} reg_bits;
};
enum max22190_delay {
MAX22190_DELAY_50US,
MAX22190_DELAY_100US,
MAX22190_DELAY_400US,
MAX22190_DELAY_800US,
MAX22190_DELAY_1800US,
MAX22190_DELAY_3200US,
MAX22190_DELAY_12800US,
MAX22190_DELAY_20000US
};
struct max22190_config {
struct spi_dt_spec spi;
struct gpio_dt_spec fault_gpio;
struct gpio_dt_spec ready_gpio;
struct gpio_dt_spec latch_gpio;
union max22190_filter filter[8];
bool crc_en;
enum max22190_mode mode;
uint8_t pkt_size;
};
struct max22190_data {
struct gpio_driver_data common;
enum max22190_ch_state channels[MAX22190_CHANNELS];
enum max22190_ch_wb_state wb[MAX22190_CHANNELS];
union max22190_cfg cfg;
union max22190_fault1 fault1;
union max22190_fault1_en fault1_en;
union max22190_fault2 fault2;
union max22190_fault2_en fault2_en;
};
/*
* @brief Compute the CRC5 value for MAX22190
* @param data - Data array to calculate CRC for.
* @return CRC result.
*/
static uint8_t max22190_crc(uint8_t *data)
{
int length = 19;
uint8_t crc_step, tmp;
uint8_t crc_init = 0x7;
uint8_t crc_poly = 0x35;
int i;
/*
* This is the C custom implementation of CRC function for MAX22190, and
* can be found here:
* https://www.analog.com/en/design-notes/guidelines-to-implement-crc-algorithm.html
*/
uint32_t datainput = (uint32_t)((data[0] << 16) + (data[1] << 8) + data[2]);
datainput = (datainput & 0xFFFFE0) + crc_init;
tmp = (uint8_t)((datainput & 0xFC0000) >> 18);
if ((tmp & 0x20) == 0x20) {
crc_step = (uint8_t)(tmp ^ crc_poly);
} else {
crc_step = tmp;
}
for (i = 0; i < length - 1; i++) {
tmp = (uint8_t)(((crc_step & 0x1F) << 1) +
((datainput >> (length - 2 - i)) & 0x01));
if ((tmp & 0x20) == 0x20) {
crc_step = (uint8_t)(tmp ^ crc_poly);
} else {
crc_step = tmp;
}
}
return (uint8_t)(crc_step & 0x1F);
}
/*
* @brief Update chan WB state in max22190_data
*
* @param dev - MAX22190 device.
* @param val - value to be set.
*/
static void max22190_update_wb_stat(const struct device *dev, uint8_t val)
{
struct max22190_data *data = dev->data;
for (int ch_n = 0; ch_n < 8; ch_n++) {
data->wb[ch_n] = (val >> ch_n) & 0x1;
}
}
/*
* @brief Update chan IN state in max22190_data
*
* @param dev - MAX22190 device.
* @param val - value to be set.
*/
static void max22190_update_in_stat(const struct device *dev, uint8_t val)
{
struct max22190_data *data = dev->data;
for (int ch_n = 0; ch_n < 8; ch_n++) {
data->channels[ch_n] = (val >> ch_n) & 0x1;
}
}
/*
* @brief Register write function for MAX22190
*
* @param dev - MAX22190 device config.
* @param addr - Register value to which data is written.
* @param val - Value which is to be written to requested register.
* @return 0 in case of success, negative error code otherwise.
*/
static int max22190_reg_transceive(const struct device *dev, uint8_t addr, uint8_t val, uint8_t rw)
{
uint8_t crc;
int ret;
uint8_t local_rx_buff[MAX22190_MAX_PKT_SIZE] = {0};
uint8_t local_tx_buff[MAX22190_MAX_PKT_SIZE] = {0};
const struct max22190_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] =
FIELD_PREP(MAX22190_ADDR_MASK, addr) | FIELD_PREP(MAX22190_RW_MASK, rw & 0x1);
local_tx_buff[1] = val;
/* If CRC enabled calculate it */
if (config->crc_en) {
local_tx_buff[2] = max22190_crc(&local_tx_buff[0]);
}
/* write cmd & read resp at once */
ret = spi_transceive_dt(&config->spi, &tx, &rx);
if (ret) {
LOG_ERR("Err spi_transcieve_dt [%d]\n", ret);
return ret;
}
/* if CRC enabled check readed */
if (config->crc_en) {
crc = max22190_crc(&local_rx_buff[0]);
if (crc != (local_rx_buff[2] & 0x1F)) {
LOG_ERR("READ CRC ERR (%d)-(%d)\n", crc, (local_rx_buff[2] & 0x1F));
return -EINVAL;
}
}
/* always (R/W) get DI reg in first byte */
max22190_update_in_stat(dev, local_rx_buff[0]);
/* in case of writing register we get as second byte WB reg */
if (rw == MAX22190_WRITE) {
max22190_update_wb_stat(dev, local_rx_buff[1]);
} else {
/* in case of READ second byte is value we are looking for */
ret = local_rx_buff[1];
}
return ret;
}
#define max22190_reg_read(dev, addr) max22190_reg_transceive(dev, addr, 0, MAX22190_READ)
#define max22190_reg_write(dev, addr, val) max22190_reg_transceive(dev, addr, val, MAX22190_WRITE)
/*
* @brief Register update function for MAX22190
*
* @param dev - MAX22190 device.
* @param addr - Register valueto wich data is updated.
* @param mask - Corresponding mask to the data that will be updated.
* @param val - Updated value to be written in the register at update.
* @return 0 in case of success, negative error code otherwise.
*/
static int max22190_reg_update(const struct device *dev, uint8_t addr, uint8_t mask, uint8_t val)
{
int ret;
uint32_t reg_val = 0;
ret = max22190_reg_read(dev, addr);
reg_val = ret;
reg_val &= ~mask;
reg_val |= mask & val;
return max22190_reg_write(dev, addr, reg_val);
}
/*
* @brief Check FAULT1 and FAULT2
*
* @param dev - MAX22190 device
*/
static void max22190_fault_check(const struct device *dev)
{
struct max22190_data *data = dev->data;
/* FAULT1 */
data->fault1.reg_raw = max22190_reg_read(dev, MAX22190_FAULT1_REG);
if (data->fault1.reg_raw) {
/* FAULT1_EN */
data->fault1_en.reg_raw = max22190_reg_read(dev, MAX22190_FAULT1_EN_REG);
PRINT_ERR_BIT(data->fault1.reg_bits.max22190_CRC,
data->fault1_en.reg_bits.max22190_CRCE);
PRINT_ERR_BIT(data->fault1.reg_bits.max22190_POR,
data->fault1_en.reg_bits.max22190_PORE);
PRINT_ERR_BIT(data->fault1.reg_bits.max22190_FAULT2,
data->fault1_en.reg_bits.max22190_FAULT2E);
PRINT_ERR_BIT(data->fault1.reg_bits.max22190_ALRMT2,
data->fault1_en.reg_bits.max22190_ALRMT2E);
PRINT_ERR_BIT(data->fault1.reg_bits.max22190_ALRMT1,
data->fault1_en.reg_bits.max22190_ALRMT1E);
PRINT_ERR_BIT(data->fault1.reg_bits.max22190_24VL,
data->fault1_en.reg_bits.max22190_24VLE);
PRINT_ERR_BIT(data->fault1.reg_bits.max22190_24VM,
data->fault1_en.reg_bits.max22190_24VME);
PRINT_ERR_BIT(data->fault1.reg_bits.max22190_WBG,
data->fault1_en.reg_bits.max22190_WBGE);
if (data->fault1.reg_bits.max22190_WBG & data->fault1_en.reg_bits.max22190_WBGE) {
uint8_t wb_val = max22190_reg_read(dev, MAX22190_WB_REG);
max22190_update_wb_stat(dev, (uint8_t)wb_val);
}
if (data->fault1.reg_bits.max22190_FAULT2) {
/* FAULT2 */
data->fault2.reg_raw = max22190_reg_read(dev, MAX22190_FAULT2_REG);
/* FAULT2_EN */
data->fault2_en.reg_raw = max22190_reg_read(dev, MAX22190_FAULT2_EN_REG);
PRINT_ERR_BIT(data->fault2.reg_bits.max22190_RFWBS,
data->fault2_en.reg_bits.max22190_RFWBSE);
PRINT_ERR_BIT(data->fault2.reg_bits.max22190_RFWBO,
data->fault2_en.reg_bits.max22190_RFWBOE);
PRINT_ERR_BIT(data->fault2.reg_bits.max22190_RFDIS,
data->fault2_en.reg_bits.max22190_RFDISE);
PRINT_ERR_BIT(data->fault2.reg_bits.max22190_RFDIO,
data->fault2_en.reg_bits.max22190_RFDIOE);
PRINT_ERR_BIT(data->fault2.reg_bits.max22190_OTSHDN,
data->fault2_en.reg_bits.max22190_OTSHDNE);
PRINT_ERR_BIT(data->fault2.reg_bits.max22190_FAULT8CK,
data->fault2_en.reg_bits.max22190_FAULT8CKE);
}
}
}
static void max22190_state_get(const struct device *dev)
{
const struct max22190_config *config = dev->config;
if (gpio_pin_get_dt(&config->fault_gpio)) {
max22190_fault_check(dev);
}
/* We are reading WB reg because on first byte will be clocked out DI reg
* on second byte we will ge WB value.
*/
uint8_t wb_val = max22190_reg_read(dev, MAX22190_WB_REG);
max22190_update_wb_stat(dev, (uint8_t)wb_val);
}
static int gpio_max22190_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_INPUT:
LOG_INF("Nothing to do, only INPUT supported");
break;
default:
LOG_ERR("On MAX22190 only input option is available!");
return -ENOTSUP;
}
return err;
}
static void max22190_filter_set(const struct device *dev)
{
const struct max22190_config *config = dev->config;
for (int ch_n = 0; ch_n < 8; ch_n++) {
max22190_reg_write(dev, MAX22190_FILTER_IN_REG(ch_n), config->filter[ch_n].reg_raw);
}
}
static int max22190_fault_set(const struct device *dev)
{
const struct max22190_data *data = dev->data;
int ret = 0;
ret = max22190_reg_write(dev, MAX22190_FAULT1_EN_REG, data->fault1_en.reg_raw);
if (ret) {
goto exit_fault_set;
}
ret = max22190_reg_write(dev, MAX22190_FAULT1_REG, data->fault1.reg_raw);
if (ret) {
goto exit_fault_set;
}
ret = max22190_reg_write(dev, MAX22190_FAULT2_EN_REG, data->fault2_en.reg_raw);
if (ret) {
goto exit_fault_set;
}
ret = max22190_reg_write(dev, MAX22190_FAULT2_REG, data->fault2.reg_raw);
if (ret) {
goto exit_fault_set;
}
return ret;
exit_fault_set:
LOG_ERR("Err spi_transcieve_dt [%d]\n", ret);
return ret;
}
static int gpio_max22190_port_get_raw(const struct device *dev, gpio_port_value_t *value)
{
const struct max22190_data *data = dev->data;
max22190_state_get(dev);
*value = 0;
for (int ch_n = 0; ch_n < 8; ch_n++) {
/* IN ch state */
*value |= data->channels[ch_n] << ch_n;
}
return 0;
}
static int gpio_max22190_init(const struct device *dev)
{
const struct max22190_config *config = dev->config;
struct max22190_data *data = dev->data;
LOG_DBG("GPIO MAX22190 init IN\n");
int err = 0;
if (!spi_is_ready_dt(&config->spi)) {
LOG_ERR("SPI bus is not ready\n");
return -ENODEV;
}
/* setup READY gpio - normal low */
if (!gpio_is_ready_dt(&config->ready_gpio)) {
LOG_ERR("READY GPIO device not ready");
return -ENODEV;
}
err = gpio_pin_configure_dt(&config->ready_gpio, GPIO_INPUT);
if (err < 0) {
LOG_ERR("Failed to configure reset GPIO");
return err;
}
/* setup FAULT gpio - normal high */
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 | GPIO_PULL_UP);
if (err < 0) {
LOG_ERR("Failed to configure DC GPIO");
return err;
}
/* setup LATCH gpio - normal high */
if (!gpio_is_ready_dt(&config->latch_gpio)) {
LOG_ERR("LATCH GPIO device not ready");
return -ENODEV;
}
err = gpio_pin_configure_dt(&config->latch_gpio, GPIO_OUTPUT_INACTIVE);
if (err < 0) {
LOG_ERR("Failed to configure busy GPIO");
return err;
}
for (int i = 0; i < 8; i++) {
LOG_DBG("IN%d WBE [%d] FBP[%d] DELAY[%d]\n", i,
config->filter[i].reg_bits.max22190_WBE,
config->filter[i].reg_bits.max22190_FBP,
config->filter[i].reg_bits.max22190_DELAY);
}
LOG_DBG(" > MAX22190 MODE: %x", config->mode);
LOG_DBG(" > MAX22190 PKT SIZE: %dbits (%dbytes)", config->pkt_size * 8, config->pkt_size);
LOG_DBG(" > MAX22190 CRC: %s", config->crc_en ? "enable" : "disable");
data->fault1_en.reg_bits.max22190_WBGE = 1;
data->fault1_en.reg_bits.max22190_PORE = 1;
/* Set all FAULT and FAULT_EN regs */
max22190_fault_set(dev);
/* Set channels filters */
max22190_filter_set(dev);
/* POR bit need to be cleared after start */
MAX22190_CLEAN_POR(dev);
LOG_DBG("GPIO MAX22190 init OUT\n");
return 0;
}
static DEVICE_API(gpio, gpio_max22190_api) = {
.pin_configure = gpio_max22190_config,
.port_get_raw = gpio_max22190_port_get_raw,
};
/* Assign appropriate value for FILTER delay*/
#define MAX22190_FILTER_SET_DELAY(delay) \
delay == 20000 ? MAX22190_DELAY_20000US \
: delay == 12800 ? MAX22190_DELAY_12800US \
: delay == 3200 ? MAX22190_DELAY_3200US \
: delay == 1600 ? MAX22190_DELAY_1800US \
: delay == 800 ? MAX22190_DELAY_800US \
: delay == 400 ? MAX22190_DELAY_400US \
: delay == 100 ? MAX22190_DELAY_100US \
: delay == 50 ? MAX22190_DELAY_50US \
: MAX22190_DELAY_20000US
/* Set FILTERx reg */
#define MAX22190_FILTER_BY_IDX(id, idx) \
{ \
.reg_bits.max22190_DELAY = \
MAX22190_FILTER_SET_DELAY(DT_INST_PROP_BY_IDX(id, filter_delays, idx)), \
.reg_bits.max22190_FBP = DT_INST_PROP_BY_IDX(id, filter_fbps, idx), \
.reg_bits.max22190_WBE = DT_INST_PROP_BY_IDX(id, filter_wbes, idx), \
}
#define GPIO_MAX22190_DEVICE(id) \
static const struct max22190_config max22190_##id##_cfg = { \
.spi = SPI_DT_SPEC_INST_GET(id, SPI_OP_MODE_MASTER | SPI_WORD_SET(8U), 0U), \
.ready_gpio = GPIO_DT_SPEC_INST_GET(id, drdy_gpios), \
.fault_gpio = GPIO_DT_SPEC_INST_GET(id, fault_gpios), \
.latch_gpio = GPIO_DT_SPEC_INST_GET(id, latch_gpios), \
.mode = DT_INST_PROP(id, max22190_mode), \
.crc_en = !(DT_INST_PROP(id, max22190_mode) & 0x1), \
.pkt_size = !(DT_INST_PROP(id, max22190_mode) & 0x1) ? 3 : 2, \
.filter = \
{ \
MAX22190_FILTER_BY_IDX(id, 0), \
MAX22190_FILTER_BY_IDX(id, 1), \
MAX22190_FILTER_BY_IDX(id, 2), \
MAX22190_FILTER_BY_IDX(id, 3), \
MAX22190_FILTER_BY_IDX(id, 4), \
MAX22190_FILTER_BY_IDX(id, 5), \
MAX22190_FILTER_BY_IDX(id, 6), \
MAX22190_FILTER_BY_IDX(id, 7), \
}, \
}; \
\
static struct max22190_data max22190_##id##_data; \
\
DEVICE_DT_INST_DEFINE(id, &gpio_max22190_init, NULL, &max22190_##id##_data, \
&max22190_##id##_cfg, POST_KERNEL, \
CONFIG_GPIO_MAX22190_INIT_PRIORITY, &gpio_max22190_api);
DT_INST_FOREACH_STATUS_OKAY(GPIO_MAX22190_DEVICE)