/* * Copyright (c) 2024 Analog Devices Inc. * Copyright (c) 2024 Baylibre SAS * * SPDX-License-Identifier: Apache-2.0 */ #ifndef ZEPHYR_DRIVERS_GPIO_GPIO_MAX149X6_H_ #define ZEPHYR_DRIVERS_GPIO_GPIO_MAX149X6_H_ #define MAX149x6_READ 0 #define MAX149x6_WRITE 1 #define MAX149X6_GET_BIT(val, i) (0x1 & ((val) >> (i))) #define PRINT_ERR_BIT(bit1, bit2) \ if ((bit1) & (bit2)) \ LOG_ERR("[%s] %d", #bit1, bit1) #define PRINT_ERR(bit) \ if (bit) \ LOG_ERR("[DIAG] [%s] %d\n", #bit, bit) #define PRINT_INF(bit) LOG_INFO("[%s] %d\n", #bit, bit) #define LOG_DIAG(...) Z_LOG(LOG_LEVEL_ERR, __VA_ARGS__) /** * @brief Compute the CRC5 value for an array of bytes when writing to MAX149X6 * @param data - array of data to encode * @param encode - action to be performed - true(encode), false(decode) * @return the resulted CRC5 */ static uint8_t max149x6_crc(uint8_t *data, bool encode) { uint8_t crc5_start = 0x1f; uint8_t crc5_poly = 0x15; uint8_t crc5_result = crc5_start; uint8_t extra_byte = 0x00; uint8_t data_bit; uint8_t result_bit; int i; /* * This is a custom implementation of a CRC5 algorithm, detailed here: * https://www.analog.com/en/app-notes/how-to-program-the-max14906-quadchannel- * industrial-digital-output-digital-input.html */ for (i = (encode) ? 0 : 2; i < 8; i++) { data_bit = (data[0] >> (7 - i)) & 0x01; result_bit = (crc5_result & 0x10) >> 4; if (data_bit ^ result_bit) { crc5_result = crc5_poly ^ ((crc5_result << 1) & 0x1f); } else { crc5_result = (crc5_result << 1) & 0x1f; } } for (i = 0; i < 8; i++) { data_bit = (data[1] >> (7 - i)) & 0x01; result_bit = (crc5_result & 0x10) >> 4; if (data_bit ^ result_bit) { crc5_result = crc5_poly ^ ((crc5_result << 1) & 0x1f); } else { crc5_result = (crc5_result << 1) & 0x1f; } } for (i = 0; i < 3; i++) { data_bit = (extra_byte >> (7 - i)) & 0x01; result_bit = (crc5_result & 0x10) >> 4; if (data_bit ^ result_bit) { crc5_result = crc5_poly ^ ((crc5_result << 1) & 0x1f); } else { crc5_result = (crc5_result << 1) & 0x1f; } } return crc5_result; } /* * @brief Register read/write function for MAX149x6 * * @param dev - MAX149x6 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 max149x6_reg_transceive(const struct device *dev, uint8_t addr, uint8_t val, uint8_t *rx_diag_buff, uint8_t rw) { uint8_t crc; int ret; uint8_t local_rx_buff[MAX149x6_MAX_PKT_SIZE] = {0}; uint8_t local_tx_buff[MAX149x6_MAX_PKT_SIZE] = {0}; const struct max149x6_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}; if (config->crc_en & 0) { rx_buf.len++; } local_tx_buff[0] = FIELD_PREP(MAX149x6_ADDR_MASK, addr) | FIELD_PREP(MAX149x6_CHIP_ADDR_MASK, config->spi_addr) | FIELD_PREP(MAX149x6_RW_MASK, rw & 0x1); local_tx_buff[1] = val; /* If CRC enabled calculate it */ if (config->crc_en) { local_tx_buff[2] = max149x6_crc(&local_tx_buff[0], true); } /* 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 = max149x6_crc(&local_rx_buff[0], false); if (crc != (local_rx_buff[2] & 0x1F)) { LOG_ERR("READ CRC ERR (%d)-(%d)\n", crc, (local_rx_buff[2] & 0x1F)); return -EINVAL; } } if (rx_diag_buff != NULL) { rx_diag_buff[0] = local_rx_buff[0]; } /* In case of write we are getting 2 diagnostic bytes - byte0 & byte1 * and pass them to diag buffer to be parsed in next stage */ if ((MAX149x6_WRITE == rw) && (rx_diag_buff != NULL)) { rx_diag_buff[1] = local_rx_buff[1]; } else { ret = local_rx_buff[1]; } return ret; } #endif