/* * Copyright (c), 2023 Basalte bv * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nxp_sc18im704_i2c #include #include #include #include #include #include #include #include LOG_MODULE_REGISTER(i2c_sc18im, CONFIG_I2C_LOG_LEVEL); #include "i2c_sc18im704.h" struct i2c_sc18im_config { const struct device *bus; uint32_t bus_speed; const struct gpio_dt_spec reset_gpios; }; struct i2c_sc18im_data { struct k_mutex lock; uint32_t i2c_config; }; int sc18im704_claim(const struct device *dev) { struct i2c_sc18im_data *data = dev->data; return k_mutex_lock(&data->lock, K_FOREVER); } int sc18im704_release(const struct device *dev) { struct i2c_sc18im_data *data = dev->data; return k_mutex_unlock(&data->lock); } int sc18im704_transfer(const struct device *dev, const uint8_t *tx_data, uint8_t tx_len, uint8_t *rx_data, uint8_t rx_len) { const struct i2c_sc18im_config *cfg = dev->config; struct i2c_sc18im_data *data = dev->data; int ret = 0; ret = k_mutex_lock(&data->lock, K_FOREVER); if (ret < 0) { return ret; } if (tx_data != NULL) { for (uint8_t i = 0; i < tx_len; ++i) { uart_poll_out(cfg->bus, tx_data[i]); } } if (rx_data != NULL) { uint64_t end; for (uint8_t i = 0; i < rx_len && ret == 0; ++i) { /* Make sure we don't wait forever */ end = sys_clock_timeout_end_calc(K_SECONDS(1)); do { ret = uart_poll_in(cfg->bus, &rx_data[i]); } while (ret == -1 && end > k_uptime_ticks()); } /* -1 indicates we timed out */ ret = ret == -1 ? -EAGAIN : ret; if (ret < 0) { LOG_ERR("Failed to read data (%d)", ret); } } k_mutex_unlock(&data->lock); return ret; } static int i2c_sc18im_configure(const struct device *dev, uint32_t config) { struct i2c_sc18im_data *data = dev->data; if (!(I2C_MODE_CONTROLLER & config)) { return -EINVAL; } if (I2C_ADDR_10_BITS & config) { return -EINVAL; } if (I2C_SPEED_GET(config) != I2C_SPEED_GET(data->i2c_config)) { uint8_t buf[] = { SC18IM704_CMD_WRITE_REG, SC18IM704_REG_I2C_CLK_L, 0, SC18IM704_CMD_STOP, }; int ret; /* CLK value is calculated as 15000000 / (8 * freq), see datasheet */ switch (I2C_SPEED_GET(config)) { case I2C_SPEED_STANDARD: buf[2] = 0x13; /* 99 kHz */ break; case I2C_SPEED_FAST: buf[2] = 0x05; /* 375 kHz */ break; default: return -EINVAL; } ret = sc18im704_transfer(dev, buf, sizeof(buf), NULL, 0); if (ret < 0) { LOG_ERR("Failed to set I2C speed (%d)", ret); return -EIO; } } data->i2c_config = config; return 0; } static int i2c_sc18im_get_config(const struct device *dev, uint32_t *config) { struct i2c_sc18im_data *data = dev->data; *config = data->i2c_config; return 0; } static int i2c_sc18im_transfer_msg(const struct device *dev, struct i2c_msg *msg, uint16_t addr) { uint8_t start[] = { SC18IM704_CMD_I2C_START, 0x00, 0x00, }; uint8_t stop = SC18IM704_CMD_STOP; int ret; if (msg->flags & I2C_MSG_ADDR_10_BITS || msg->len > UINT8_MAX) { return -EINVAL; } start[1] = addr | (msg->flags & I2C_MSG_RW_MASK); start[2] = msg->len; ret = sc18im704_transfer(dev, start, sizeof(start), NULL, 0); if (ret < 0) { return ret; } if (msg->flags & I2C_MSG_READ) { /* Send the stop character before reading */ ret = sc18im704_transfer(dev, &stop, 1, msg->buf, msg->len); if (ret < 0) { return ret; } } else { ret = sc18im704_transfer(dev, msg->buf, msg->len, NULL, 0); if (ret < 0) { return ret; } if (msg->flags & I2C_MSG_STOP) { ret = sc18im704_transfer(dev, &stop, 1, NULL, 0); if (ret < 0) { return ret; } } } return 0; } static int i2c_sc18im_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs, uint16_t addr) { int ret; if (num_msgs == 0) { return 0; } ret = sc18im704_claim(dev); if (ret < 0) { LOG_ERR("Failed to claim I2C bridge (%d)", ret); return ret; } for (uint8_t i = 0; i < num_msgs && ret == 0; ++i) { ret = i2c_sc18im_transfer_msg(dev, &msgs[i], addr); } #ifdef CONFIG_I2C_SC18IM704_VERIFY if (ret == 0) { uint8_t buf[] = { SC18IM704_CMD_READ_REG, SC18IM704_REG_I2C_STAT, SC18IM704_CMD_STOP, }; uint8_t data; ret = sc18im704_transfer(dev, buf, sizeof(buf), &data, 1); if (ret == 0 && data != SC18IM704_I2C_STAT_OK) { ret = -EIO; } } #endif /* CONFIG_I2C_SC18IM704_VERIFY */ sc18im704_release(dev); return ret; } static int i2c_sc18im_init(const struct device *dev) { const struct i2c_sc18im_config *cfg = dev->config; struct i2c_sc18im_data *data = dev->data; int ret; /* The device baudrate after reset is 9600 */ struct uart_config uart_cfg = { .baudrate = 9600, .parity = UART_CFG_PARITY_NONE, .stop_bits = UART_CFG_STOP_BITS_1, .data_bits = UART_CFG_DATA_BITS_8, .flow_ctrl = UART_CFG_FLOW_CTRL_NONE, }; k_mutex_init(&data->lock); if (!device_is_ready(cfg->bus)) { LOG_ERR("UART bus not ready"); return -ENODEV; } ret = uart_configure(cfg->bus, &uart_cfg); if (ret < 0) { LOG_ERR("Failed to configure UART (%d)", ret); return ret; } if (cfg->reset_gpios.port) { uint8_t buf[2]; if (!device_is_ready(cfg->reset_gpios.port)) { LOG_ERR("Reset GPIO device not ready"); return -ENODEV; } ret = gpio_pin_configure_dt(&cfg->reset_gpios, GPIO_OUTPUT_ACTIVE); if (ret < 0) { LOG_ERR("Failed to configure reset GPIO (%d)", ret); return ret; } ret = gpio_pin_set_dt(&cfg->reset_gpios, 0); if (ret < 0) { LOG_ERR("Failed to set reset GPIO (%d)", ret); return ret; } /* The device sends "OK" */ ret = sc18im704_transfer(dev, NULL, 0, buf, sizeof(buf)); if (ret < 0) { LOG_ERR("Failed to get OK (%d)", ret); return ret; } } if (cfg->bus_speed != 9600) { uint16_t brg = (7372800 / cfg->bus_speed) - 16; uint8_t buf[] = { SC18IM704_CMD_WRITE_REG, SC18IM704_REG_BRG0, brg & 0xff, SC18IM704_REG_BRG1, brg >> 8, SC18IM704_CMD_STOP, }; ret = sc18im704_transfer(dev, buf, sizeof(buf), NULL, 0); if (ret < 0) { LOG_ERR("Failed to set baudrate (%d)", ret); return ret; } /* Make sure UART buffer is sent */ k_msleep(1); /* Re-configure the UART controller with the new baudrate */ uart_cfg.baudrate = cfg->bus_speed; ret = uart_configure(cfg->bus, &uart_cfg); if (ret < 0) { LOG_ERR("Failed to re-configure UART (%d)", ret); return ret; } } return 0; } static const struct i2c_driver_api i2c_sc18im_driver_api = { .configure = i2c_sc18im_configure, .get_config = i2c_sc18im_get_config, .transfer = i2c_sc18im_transfer, }; #define I2C_SC18IM_DEFINE(n) \ \ static const struct i2c_sc18im_config i2c_sc18im_config_##n = { \ .bus = DEVICE_DT_GET(DT_BUS(DT_INST_PARENT(n))), \ .bus_speed = DT_PROP_OR(DT_INST_PARENT(n), target_speed, 9600), \ .reset_gpios = GPIO_DT_SPEC_GET_OR(DT_INST_PARENT(n), reset_gpios, {0}), \ }; \ static struct i2c_sc18im_data i2c_sc18im_data_##n = { \ .i2c_config = I2C_MODE_CONTROLLER | (I2C_SPEED_STANDARD << I2C_SPEED_SHIFT), \ }; \ \ DEVICE_DT_INST_DEFINE(n, i2c_sc18im_init, NULL, \ &i2c_sc18im_data_##n, &i2c_sc18im_config_##n, \ POST_KERNEL, CONFIG_I2C_SC18IM704_INIT_PRIORITY, \ &i2c_sc18im_driver_api); DT_INST_FOREACH_STATUS_OKAY(I2C_SC18IM_DEFINE)