1 /*
2  * Copyright (c) 2024 Analog Devices Inc.
3  * Copyright (c) 2024 Baylibre SAS
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #ifndef ZEPHYR_DRIVERS_GPIO_GPIO_MAX149X6_H_
9 #define ZEPHYR_DRIVERS_GPIO_GPIO_MAX149X6_H_
10 
11 #define MAX149x6_READ  0
12 #define MAX149x6_WRITE 1
13 
14 #define MAX149X6_GET_BIT(val, i) (0x1 & ((val) >> (i)))
15 #define PRINT_ERR_BIT(bit1, bit2)                                                                  \
16 	if ((bit1) & (bit2))                                                                       \
17 		LOG_ERR("[%s] %d", #bit1, bit1)
18 #define PRINT_ERR(bit)                                                                             \
19 	if (bit)                                                                                   \
20 		LOG_ERR("[DIAG] [%s] %d\n", #bit, bit)
21 #define PRINT_INF(bit) LOG_INFO("[%s] %d\n", #bit, bit)
22 #define LOG_DIAG(...)  Z_LOG(LOG_LEVEL_ERR, __VA_ARGS__)
23 
24 /**
25  * @brief Compute the CRC5 value for an array of bytes when writing to MAX149X6
26  * @param data - array of data to encode
27  * @param encode - action to be performed - true(encode), false(decode)
28  * @return the resulted CRC5
29  */
max149x6_crc(uint8_t * data,bool encode)30 static uint8_t max149x6_crc(uint8_t *data, bool encode)
31 {
32 	uint8_t crc5_start = 0x1f;
33 	uint8_t crc5_poly = 0x15;
34 	uint8_t crc5_result = crc5_start;
35 	uint8_t extra_byte = 0x00;
36 	uint8_t data_bit;
37 	uint8_t result_bit;
38 	int i;
39 
40 	/*
41 	 * This is a custom implementation of a CRC5 algorithm, detailed here:
42 	 * https://www.analog.com/en/app-notes/how-to-program-the-max14906-quadchannel-
43 	 *					industrial-digital-output-digital-input.html
44 	 */
45 
46 	for (i = (encode) ? 0 : 2; i < 8; i++) {
47 		data_bit = (data[0] >> (7 - i)) & 0x01;
48 		result_bit = (crc5_result & 0x10) >> 4;
49 		if (data_bit ^ result_bit) {
50 			crc5_result = crc5_poly ^ ((crc5_result << 1) & 0x1f);
51 		} else {
52 			crc5_result = (crc5_result << 1) & 0x1f;
53 		}
54 	}
55 
56 	for (i = 0; i < 8; i++) {
57 		data_bit = (data[1] >> (7 - i)) & 0x01;
58 		result_bit = (crc5_result & 0x10) >> 4;
59 		if (data_bit ^ result_bit) {
60 			crc5_result = crc5_poly ^ ((crc5_result << 1) & 0x1f);
61 		} else {
62 			crc5_result = (crc5_result << 1) & 0x1f;
63 		}
64 	}
65 
66 	for (i = 0; i < 3; i++) {
67 		data_bit = (extra_byte >> (7 - i)) & 0x01;
68 		result_bit = (crc5_result & 0x10) >> 4;
69 		if (data_bit ^ result_bit) {
70 			crc5_result = crc5_poly ^ ((crc5_result << 1) & 0x1f);
71 		} else {
72 			crc5_result = (crc5_result << 1) & 0x1f;
73 		}
74 	}
75 
76 	return crc5_result;
77 }
78 
79 /*
80  * @brief Register read/write function for MAX149x6
81  *
82  * @param dev - MAX149x6 device config.
83  * @param addr - Register value to which data is written.
84  * @param val - Value which is to be written to requested register.
85  * @return 0 in case of success, negative error code otherwise.
86  */
max149x6_reg_transceive(const struct device * dev,uint8_t addr,uint8_t val,uint8_t * rx_diag_buff,uint8_t rw)87 static int max149x6_reg_transceive(const struct device *dev, uint8_t addr, uint8_t val,
88 				   uint8_t *rx_diag_buff, uint8_t rw)
89 {
90 	uint8_t crc;
91 	int ret;
92 
93 	uint8_t local_rx_buff[MAX149x6_MAX_PKT_SIZE] = {0};
94 	uint8_t local_tx_buff[MAX149x6_MAX_PKT_SIZE] = {0};
95 
96 	const struct max149x6_config *config = dev->config;
97 
98 	struct spi_buf tx_buf = {
99 		.buf = &local_tx_buff,
100 		.len = config->pkt_size,
101 	};
102 	const struct spi_buf_set tx = {.buffers = &tx_buf, .count = 1};
103 
104 	struct spi_buf rx_buf = {
105 		.buf = &local_rx_buff,
106 		.len = config->pkt_size,
107 	};
108 	const struct spi_buf_set rx = {.buffers = &rx_buf, .count = 1};
109 
110 	if (config->crc_en & 0) {
111 		rx_buf.len++;
112 	}
113 
114 	local_tx_buff[0] = FIELD_PREP(MAX149x6_ADDR_MASK, addr) |
115 			   FIELD_PREP(MAX149x6_CHIP_ADDR_MASK, config->spi_addr) |
116 			   FIELD_PREP(MAX149x6_RW_MASK, rw & 0x1);
117 	local_tx_buff[1] = val;
118 
119 	/* If CRC enabled calculate it */
120 	if (config->crc_en) {
121 		local_tx_buff[2] = max149x6_crc(&local_tx_buff[0], true);
122 	}
123 
124 	/* write cmd & read resp at once */
125 	ret = spi_transceive_dt(&config->spi, &tx, &rx);
126 
127 	if (ret) {
128 		LOG_ERR("Err spi_transcieve_dt  [%d]\n", ret);
129 		return ret;
130 	}
131 
132 	/* if CRC enabled check readed */
133 	if (config->crc_en) {
134 		crc = max149x6_crc(&local_rx_buff[0], false);
135 		if (crc != (local_rx_buff[2] & 0x1F)) {
136 			LOG_ERR("READ CRC ERR (%d)-(%d)\n", crc, (local_rx_buff[2] & 0x1F));
137 			return -EINVAL;
138 		}
139 	}
140 
141 	if (rx_diag_buff != NULL) {
142 		rx_diag_buff[0] = local_rx_buff[0];
143 	}
144 
145 	/* In case of write we are getting 2 diagnostic bytes - byte0 & byte1
146 	 * and pass them to diag buffer to be parsed in next stage
147 	 */
148 	if ((MAX149x6_WRITE == rw) && (rx_diag_buff != NULL)) {
149 		rx_diag_buff[1] = local_rx_buff[1];
150 	} else {
151 		ret = local_rx_buff[1];
152 	}
153 
154 	return ret;
155 }
156 
157 #endif
158