|
| 1 | +/* |
| 2 | + * Copyright (c) 2025 Analog Devices Inc. |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: Apache-2.0 |
| 5 | + */ |
| 6 | + |
| 7 | +#include <zephyr/drivers/gpio.h> |
| 8 | +#include <zephyr/drivers/spi.h> |
| 9 | +#include <zephyr/kernel.h> |
| 10 | +#include <zephyr/sys/byteorder.h> |
| 11 | +#include <zephyr/sys/crc.h> |
| 12 | + |
| 13 | +#define LOG_LEVEL CONFIG_GPIO_LOG_LEVEL |
| 14 | +#include <zephyr/logging/log.h> |
| 15 | + |
| 16 | +LOG_MODULE_REGISTER(gpio_max14917); |
| 17 | + |
| 18 | +#include <zephyr/drivers/gpio/gpio_utils.h> |
| 19 | + |
| 20 | +#include "gpio_max14917.h" |
| 21 | + |
| 22 | +#define DT_DRV_COMPAT adi_max14917_gpio |
| 23 | + |
| 24 | +static int max14917_reg_trans_spi_diag(const struct device *dev) |
| 25 | +{ |
| 26 | + int ret = 0; |
| 27 | + uint8_t crc; |
| 28 | + |
| 29 | + uint8_t local_tx_buff[MAX14917_MAX_PKT_SIZE] = {0}; |
| 30 | + uint8_t local_rx_buff[MAX14917_MAX_PKT_SIZE] = {0}; |
| 31 | + |
| 32 | + struct max14917_data *data = dev->data; |
| 33 | + const struct max14917_config *config = dev->config; |
| 34 | + |
| 35 | + struct spi_buf tx_buf = { |
| 36 | + .buf = &local_tx_buff, |
| 37 | + .len = config->pkt_size, |
| 38 | + }; |
| 39 | + const struct spi_buf_set tx = {.buffers = &tx_buf, .count = 1}; |
| 40 | + |
| 41 | + struct spi_buf rx_buf = { |
| 42 | + .buf = &local_rx_buff, |
| 43 | + .len = config->pkt_size, |
| 44 | + }; |
| 45 | + const struct spi_buf_set rx = {.buffers = &rx_buf, .count = 1}; |
| 46 | + |
| 47 | + local_tx_buff[0] = data->gpios_ON; |
| 48 | + |
| 49 | + /* If CRC enabled calculate it */ |
| 50 | + if (config->crc_en) { |
| 51 | + crc = crc8(&local_tx_buff[0], 1, MAX14917_CRC_POLY, MAX14917_CRC_INI_VAL, false); |
| 52 | + local_tx_buff[1] = (crc & MAX14917_CRC_MASK); |
| 53 | + } |
| 54 | + |
| 55 | + /* Perform SPI transaction */ |
| 56 | + ret = spi_transceive_dt(&config->spi, &tx, &rx); |
| 57 | + if (ret) { |
| 58 | + LOG_ERR("SPI transfer failed"); |
| 59 | + return ret; |
| 60 | + } |
| 61 | + |
| 62 | + /* If CRC enabled check it */ |
| 63 | + if (config->crc_en) { |
| 64 | + crc = crc8(&local_tx_buff[0], 1, MAX14917_CRC_POLY, MAX14917_CRC_INI_VAL, false); |
| 65 | + crc = (crc & MAX14917_CRC_MASK); |
| 66 | + if (crc != (local_rx_buff[1] & 0x1F)) { |
| 67 | + LOG_ERR("READ CRC ERR (%d)-(%d)\n", crc, (local_rx_buff[1] & 0x1F)); |
| 68 | + return -EINVAL; |
| 69 | + } |
| 70 | + /* Set error flags in device data */ |
| 71 | + data->comm_err = (local_rx_buff[1] & MAX14917_COMM_ERR); |
| 72 | + data->verr = (local_rx_buff[1] & MAX14917_VERR); |
| 73 | + data->therm_err = (local_rx_buff[1] & MAX14917_THERM_ERR); |
| 74 | + } |
| 75 | + |
| 76 | + data->gpios_fault = local_rx_buff[0]; |
| 77 | + |
| 78 | + return 0; |
| 79 | +} |
| 80 | + |
| 81 | +static int max14917_fault_check(const struct device *dev) |
| 82 | +{ |
| 83 | + int ret; |
| 84 | + const struct max14917_data *data = dev->data; |
| 85 | + const struct max14917_config *config = dev->config; |
| 86 | + |
| 87 | + if (gpio_pin_get_dt(&config->fault_gpio)) { |
| 88 | + LOG_DBG("FAULT GPIO is high"); |
| 89 | + } |
| 90 | + |
| 91 | + /* Update error flags */ |
| 92 | + ret = max14917_reg_trans_spi_diag(dev); |
| 93 | + if (ret) { |
| 94 | + return ret; |
| 95 | + } |
| 96 | + |
| 97 | + if (data->comm_err) { |
| 98 | + LOG_DBG("COMMERR flag is active"); |
| 99 | + } |
| 100 | + if (data->verr) { |
| 101 | + LOG_DBG("VERR flag is active"); |
| 102 | + } |
| 103 | + if (data->therm_err) { |
| 104 | + LOG_DBG("THERMERR flag is active"); |
| 105 | + } |
| 106 | + |
| 107 | + for (int i = 0; i < MAX14917_CHANNELS; i++) { |
| 108 | + if (data->gpios_fault & BIT(i)) { |
| 109 | + LOG_DBG("Channel %d has a fault", i); |
| 110 | + } |
| 111 | + } |
| 112 | + |
| 113 | + return 0; |
| 114 | +} |
| 115 | + |
| 116 | +static int gpio_max14917_init(const struct device *dev) |
| 117 | +{ |
| 118 | + struct max14917_data *data = dev->data; |
| 119 | + const struct max14917_config *config = dev->config; |
| 120 | + int err = 0; |
| 121 | + |
| 122 | + LOG_DBG(" --- GPIO max14917 init IN ---"); |
| 123 | + |
| 124 | + if (!spi_is_ready_dt(&config->spi)) { |
| 125 | + LOG_ERR("SPI bus is not ready\n"); |
| 126 | + return -ENODEV; |
| 127 | + } |
| 128 | + |
| 129 | + /* Output GPIOS */ |
| 130 | + /* setup EN gpio - normal low */ |
| 131 | + if (!gpio_is_ready_dt(&config->en_gpio)) { |
| 132 | + LOG_ERR("EN GPIO device not ready"); |
| 133 | + return -ENODEV; |
| 134 | + } |
| 135 | + |
| 136 | + err = gpio_pin_configure_dt(&config->en_gpio, GPIO_OUTPUT); |
| 137 | + if (err < 0) { |
| 138 | + LOG_ERR("Failed to configure EN GPIO"); |
| 139 | + return err; |
| 140 | + } |
| 141 | + |
| 142 | + /* setup SYNC gpio - normal low */ |
| 143 | + if (!gpio_is_ready_dt(&config->sync_gpio)) { |
| 144 | + LOG_ERR("SYNC GPIO device not ready"); |
| 145 | + return -ENODEV; |
| 146 | + } |
| 147 | + |
| 148 | + err = gpio_pin_configure_dt(&config->sync_gpio, GPIO_OUTPUT); |
| 149 | + if (err < 0) { |
| 150 | + LOG_ERR("Failed to configure SYNC GPIO"); |
| 151 | + return err; |
| 152 | + } |
| 153 | + /* setup CRCEN gpio - normal low */ |
| 154 | + if (!gpio_is_ready_dt(&config->crcen_gpio)) { |
| 155 | + LOG_ERR("CRCEN GPIO device not ready"); |
| 156 | + return -ENODEV; |
| 157 | + } |
| 158 | + |
| 159 | + err = gpio_pin_configure_dt(&config->crcen_gpio, GPIO_OUTPUT); |
| 160 | + if (err < 0) { |
| 161 | + LOG_ERR("Failed to configure CRCEN GPIO"); |
| 162 | + return err; |
| 163 | + } |
| 164 | + /* Input GPIOS */ |
| 165 | + /* setup VDDOK gpio - normal low */ |
| 166 | + if (!gpio_is_ready_dt(&config->vddok_gpio)) { |
| 167 | + LOG_ERR("VDDOK GPIO device not ready"); |
| 168 | + return -ENODEV; |
| 169 | + } |
| 170 | + |
| 171 | + err = gpio_pin_configure_dt(&config->vddok_gpio, GPIO_INPUT); |
| 172 | + if (err < 0) { |
| 173 | + LOG_ERR("Failed to configure VDDOK GPIO"); |
| 174 | + return err; |
| 175 | + } |
| 176 | + /* setup READY gpio - normal low */ |
| 177 | + if (!gpio_is_ready_dt(&config->ready_gpio)) { |
| 178 | + LOG_ERR("VDDOK READY device not ready"); |
| 179 | + return -ENODEV; |
| 180 | + } |
| 181 | + |
| 182 | + err = gpio_pin_configure_dt(&config->ready_gpio, GPIO_INPUT); |
| 183 | + if (err < 0) { |
| 184 | + LOG_ERR("Failed to configure READY GPIO"); |
| 185 | + return err; |
| 186 | + } |
| 187 | + /* setup COMERR gpio - normal low */ |
| 188 | + if (!gpio_is_ready_dt(&config->comerr_gpio)) { |
| 189 | + LOG_ERR("COMERR GPIO device not ready"); |
| 190 | + return -ENODEV; |
| 191 | + } |
| 192 | + |
| 193 | + err = gpio_pin_configure_dt(&config->comerr_gpio, GPIO_INPUT); |
| 194 | + if (err < 0) { |
| 195 | + LOG_ERR("Failed to configure COMERR GPIO"); |
| 196 | + return err; |
| 197 | + } |
| 198 | + /* setup FAULT gpio - normal low */ |
| 199 | + if (!gpio_is_ready_dt(&config->fault_gpio)) { |
| 200 | + LOG_ERR("FAULT GPIO device not ready"); |
| 201 | + return -ENODEV; |
| 202 | + } |
| 203 | + |
| 204 | + err = gpio_pin_configure_dt(&config->fault_gpio, GPIO_INPUT); |
| 205 | + if (err < 0) { |
| 206 | + LOG_ERR("Failed to configure FAULT GPIO"); |
| 207 | + return err; |
| 208 | + } |
| 209 | + |
| 210 | + err = gpio_pin_set_dt(&config->en_gpio, 1); |
| 211 | + if (err) { |
| 212 | + return err; |
| 213 | + } |
| 214 | + err = gpio_pin_set_dt(&config->sync_gpio, 1); |
| 215 | + if (err) { |
| 216 | + return err; |
| 217 | + } |
| 218 | + |
| 219 | + if (config->crc_en) { |
| 220 | + err = gpio_pin_set_dt(&config->crcen_gpio, 1); |
| 221 | + } else { |
| 222 | + err = gpio_pin_set_dt(&config->crcen_gpio, 0); |
| 223 | + } |
| 224 | + |
| 225 | + if (err) { |
| 226 | + return err; |
| 227 | + } |
| 228 | + |
| 229 | + /* Initialize satus and fault flags to 0 */ |
| 230 | + data->gpios_ON = 0; |
| 231 | + data->gpios_fault = 0; |
| 232 | + |
| 233 | + err = max14917_fault_check(dev); |
| 234 | + |
| 235 | + return err; |
| 236 | +} |
| 237 | + |
| 238 | +static int gpio_max14917_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) |
| 239 | +{ |
| 240 | + int err = 0; |
| 241 | + |
| 242 | + if ((flags & (GPIO_INPUT | GPIO_OUTPUT)) == GPIO_DISCONNECTED) { |
| 243 | + return -ENOTSUP; |
| 244 | + } |
| 245 | + |
| 246 | + if ((flags & GPIO_SINGLE_ENDED) != 0) { |
| 247 | + return -ENOTSUP; |
| 248 | + } |
| 249 | + |
| 250 | + if ((flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) != 0) { |
| 251 | + return -ENOTSUP; |
| 252 | + } |
| 253 | + |
| 254 | + if (flags & GPIO_INT_ENABLE) { |
| 255 | + return -ENOTSUP; |
| 256 | + } |
| 257 | + |
| 258 | + switch (flags & GPIO_DIR_MASK) { |
| 259 | + case GPIO_OUTPUT: |
| 260 | + break; |
| 261 | + case GPIO_INPUT: |
| 262 | + default: |
| 263 | + LOG_ERR("NOT SUPPORTED OPTION!"); |
| 264 | + return -ENOTSUP; |
| 265 | + } |
| 266 | + |
| 267 | + return err; |
| 268 | +} |
| 269 | + |
| 270 | +static int gpio_max14917_port_get_raw(const struct device *dev, gpio_port_value_t *value) |
| 271 | +{ |
| 272 | + int ret; |
| 273 | + const struct max14917_data *data = dev->data; |
| 274 | + |
| 275 | + ret = max14917_fault_check(dev); |
| 276 | + if (ret) { |
| 277 | + return ret; |
| 278 | + } |
| 279 | + |
| 280 | + *value = data->gpios_ON; |
| 281 | + |
| 282 | + return 0; |
| 283 | +} |
| 284 | + |
| 285 | +static int gpio_max14917_port_set_bits_raw(const struct device *dev, gpio_port_pins_t pins) |
| 286 | +{ |
| 287 | + int ret; |
| 288 | + struct max14917_data *data = dev->data; |
| 289 | + |
| 290 | + ret = max14917_fault_check(dev); |
| 291 | + if (ret) { |
| 292 | + return ret; |
| 293 | + } |
| 294 | + |
| 295 | + data->gpios_ON = data->gpios_ON | pins; |
| 296 | + |
| 297 | + return max14917_reg_trans_spi_diag(dev); |
| 298 | +} |
| 299 | + |
| 300 | +static int gpio_max14917_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t pins) |
| 301 | +{ |
| 302 | + int ret; |
| 303 | + struct max14917_data *data = dev->data; |
| 304 | + |
| 305 | + ret = max14917_fault_check(dev); |
| 306 | + if (ret) { |
| 307 | + return ret; |
| 308 | + } |
| 309 | + |
| 310 | + data->gpios_ON = data->gpios_ON & ~pins; |
| 311 | + |
| 312 | + return max14917_reg_trans_spi_diag(dev); |
| 313 | +} |
| 314 | + |
| 315 | +static int gpio_max14917_port_toggle_bits(const struct device *dev, gpio_port_pins_t pins) |
| 316 | +{ |
| 317 | + int ret; |
| 318 | + struct max14917_data *data = dev->data; |
| 319 | + |
| 320 | + ret = max14917_fault_check(dev); |
| 321 | + if (ret) { |
| 322 | + return ret; |
| 323 | + } |
| 324 | + |
| 325 | + data->gpios_ON ^= pins; |
| 326 | + |
| 327 | + return max14917_reg_trans_spi_diag(dev); |
| 328 | +} |
| 329 | + |
| 330 | +static DEVICE_API(gpio, gpio_max14917_api) = { |
| 331 | + .pin_configure = gpio_max14917_config, |
| 332 | + .port_get_raw = gpio_max14917_port_get_raw, |
| 333 | + .port_set_bits_raw = gpio_max14917_port_set_bits_raw, |
| 334 | + .port_clear_bits_raw = gpio_max14917_port_clear_bits_raw, |
| 335 | + .port_toggle_bits = gpio_max14917_port_toggle_bits, |
| 336 | +}; |
| 337 | + |
| 338 | +#define GPIO_MAX14917_DEVICE(id) \ |
| 339 | + static const struct max14917_config max14917_##id##_cfg = { \ |
| 340 | + .spi = SPI_DT_SPEC_INST_GET(id, SPI_OP_MODE_MASTER | SPI_WORD_SET(8U), 0U), \ |
| 341 | + .vddok_gpio = GPIO_DT_SPEC_INST_GET(id, vddok_gpios), \ |
| 342 | + .ready_gpio = GPIO_DT_SPEC_INST_GET(id, ready_gpios), \ |
| 343 | + .comerr_gpio = GPIO_DT_SPEC_INST_GET(id, comerr_gpios), \ |
| 344 | + .fault_gpio = GPIO_DT_SPEC_INST_GET(id, fault_gpios), \ |
| 345 | + .en_gpio = GPIO_DT_SPEC_INST_GET(id, en_gpios), \ |
| 346 | + .sync_gpio = GPIO_DT_SPEC_INST_GET(id, sync_gpios), \ |
| 347 | + .crcen_gpio = GPIO_DT_SPEC_INST_GET(id, crcen_gpios), \ |
| 348 | + .crc_en = DT_INST_PROP(id, crc_en), \ |
| 349 | + .pkt_size = (DT_INST_PROP(id, crc_en) & 0x1) ? 2 : 1, \ |
| 350 | + }; \ |
| 351 | + \ |
| 352 | + static struct max14917_data max14917_##id##_data; \ |
| 353 | + \ |
| 354 | + DEVICE_DT_INST_DEFINE(id, &gpio_max14917_init, NULL, &max14917_##id##_data, \ |
| 355 | + &max14917_##id##_cfg, POST_KERNEL, \ |
| 356 | + CONFIG_GPIO_MAX14917_INIT_PRIORITY, &gpio_max14917_api); |
| 357 | + |
| 358 | +DT_INST_FOREACH_STATUS_OKAY(GPIO_MAX14917_DEVICE) |
0 commit comments