| /* ist8310.c - Driver for Isentek IST8310 Geomagnetic Sensor */ |
| |
| /* |
| * Copyright (c) 2023 NXP Semiconductors |
| * Copyright (c) 2023 Cognipilot Foundation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/sys/byteorder.h> |
| #include <zephyr/logging/log.h> |
| #include "ist8310.h" |
| |
| LOG_MODULE_REGISTER(IST8310, CONFIG_SENSOR_LOG_LEVEL); |
| |
| static inline int ist8310_bus_check(const struct device *dev) |
| { |
| const struct ist8310_config *cfg = dev->config; |
| |
| return cfg->bus_io->check(&cfg->bus); |
| } |
| |
| static inline int ist8310_reg_read(const struct device *dev, uint8_t start, uint8_t *buf, int size) |
| { |
| const struct ist8310_config *cfg = dev->config; |
| |
| return cfg->bus_io->read(&cfg->bus, start, buf, size); |
| } |
| |
| static inline int ist8310_reg_write(const struct device *dev, uint8_t reg, uint8_t val) |
| { |
| const struct ist8310_config *cfg = dev->config; |
| |
| return cfg->bus_io->write(&cfg->bus, reg, val); |
| } |
| |
| static int ist8310_sample_fetch(const struct device *dev, enum sensor_channel chan) |
| { |
| |
| struct ist8310_data *drv_data = dev->data; |
| uint8_t buff[6]; |
| |
| if (ist8310_reg_read(dev, IST8310_STATUS_REGISTER1, (uint8_t *)buff, 1) < 0) { |
| LOG_ERR("failed to read status register 1"); |
| return -EIO; |
| } |
| |
| if ((buff[0] & STAT1_DRDY) == 0) { |
| LOG_ERR("Data not ready"); |
| if (ist8310_reg_write(dev, IST8310_CONTROL_REGISTER1, CTRL1_MODE_SINGLE) < 0) { |
| LOG_ERR("failed to set single"); |
| return -EIO; |
| } |
| return -EIO; |
| } |
| |
| if (ist8310_reg_read(dev, IST8310_OUTPUT_VALUE_X_L, (uint8_t *)buff, 6) < 0) { |
| LOG_ERR("failed to read mag values"); |
| return -EIO; |
| } |
| |
| drv_data->sample_x = (sys_le16_to_cpu(*(uint16_t *)&buff[0])); |
| drv_data->sample_y = (sys_le16_to_cpu(*(uint16_t *)&buff[2])); |
| drv_data->sample_z = (sys_le16_to_cpu(*(uint16_t *)&buff[4])); |
| |
| if (ist8310_reg_write(dev, IST8310_CONTROL_REGISTER1, CTRL1_MODE_SINGLE) < 0) { |
| LOG_ERR("failed to set single"); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int ist8310_channel_get(const struct device *dev, enum sensor_channel chan, |
| struct sensor_value *val) |
| { |
| struct ist8310_data *drv_data = dev->data; |
| |
| switch (chan) { |
| case SENSOR_CHAN_MAGN_X: |
| sensor_value_from_float(val, drv_data->sample_x * (1.0f / 1320)); |
| break; |
| case SENSOR_CHAN_MAGN_Y: |
| sensor_value_from_float(val, drv_data->sample_y * (1.0f / 1320)); |
| break; |
| case SENSOR_CHAN_MAGN_Z: |
| sensor_value_from_float(val, drv_data->sample_z * (1.0f / 1320)); |
| break; |
| case SENSOR_CHAN_MAGN_XYZ: |
| sensor_value_from_float(val, drv_data->sample_x * (1.0f / 1320)); |
| sensor_value_from_float(val + 1, drv_data->sample_y * (1.0f / 1320)); |
| sensor_value_from_float(val + 2, drv_data->sample_z * (1.0f / 1320)); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static const struct sensor_driver_api ist8310_api_funcs = { |
| .sample_fetch = ist8310_sample_fetch, |
| .channel_get = ist8310_channel_get, |
| }; |
| |
| static int ist8310_init_chip(const struct device *dev) |
| { |
| uint8_t reg; |
| |
| /* Read chip ID (can only be read in sleep mode)*/ |
| if (ist8310_reg_read(dev, IST8310_WHO_AM_I, ®, 1) < 0) { |
| LOG_ERR("failed reading chip id"); |
| return -EIO; |
| } |
| |
| if (ist8310_reg_read(dev, IST8310_WHO_AM_I, ®, 1) < 0) { |
| LOG_ERR("failed reading chip id"); |
| return -EIO; |
| } |
| |
| if (ist8310_reg_read(dev, IST8310_WHO_AM_I, ®, 1) < 0) { |
| LOG_ERR("failed reading chip id"); |
| return -EIO; |
| } |
| |
| if (reg != IST8310_WHO_AM_I_VALUE) { |
| LOG_ERR("invalid chip id 0x%x", reg); |
| return -EIO; |
| } |
| |
| if (ist8310_reg_read(dev, IST8310_CONTROL_REGISTER2, ®, 1) < 0) { |
| LOG_ERR("failed reading chip reg2"); |
| return -EIO; |
| } |
| |
| reg &= ~CTRL2_SRST; |
| |
| if (ist8310_reg_write(dev, IST8310_CONTROL_REGISTER2, reg) < 0) { |
| LOG_ERR("failed to set REG2 to %d", reg); |
| return -EIO; |
| } |
| |
| k_sleep(K_MSEC(3)); |
| |
| if (ist8310_reg_read(dev, IST8310_CONTROL_REGISTER3, ®, 1) < 0) { |
| LOG_ERR("failed reading chip reg3"); |
| return -EIO; |
| } |
| |
| reg |= X_16BIT | Y_16BIT | Z_16BIT; |
| |
| if (ist8310_reg_write(dev, IST8310_CONTROL_REGISTER3, reg) < 0) { |
| LOG_ERR("failed to set REG3 to %d", reg); |
| return -EIO; |
| } |
| |
| if (ist8310_reg_write(dev, IST8310_AVG_REGISTER, XZ_16TIMES_CLEAR | Y_16TIMES_CLEAR) < 0) { |
| LOG_ERR("failed to set AVG"); |
| return -EIO; |
| } |
| |
| if (ist8310_reg_write(dev, IST8310_AVG_REGISTER, XZ_16TIMES_SET | Y_16TIMES_SET) < 0) { |
| LOG_ERR("failed to set AVG"); |
| return -EIO; |
| } |
| |
| if (ist8310_reg_write(dev, IST8310_PDCNTL_REGISTER, PULSE_NORMAL) < 0) { |
| LOG_ERR("failed to set AVG"); |
| return -EIO; |
| } |
| |
| k_sleep(K_MSEC(3)); |
| |
| if (ist8310_reg_write(dev, IST8310_CONTROL_REGISTER1, CTRL1_MODE_SINGLE) < 0) { |
| LOG_ERR("failed to set single"); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int ist8310_init(const struct device *dev) |
| { |
| int err = 0; |
| |
| err = ist8310_bus_check(dev); |
| if (err < 0) { |
| LOG_DBG("bus check failed: %d", err); |
| return err; |
| } |
| |
| if (ist8310_init_chip(dev) < 0) { |
| LOG_ERR("failed to initialize chip"); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| /* Initializes a struct ist8310_config for an instance on an I2C bus. */ |
| #define IST8310_CONFIG_I2C(inst) \ |
| .bus.i2c = I2C_DT_SPEC_INST_GET(inst), .bus_io = &ist8310_bus_io_i2c, |
| |
| #define IST8310_BUS_CFG(inst) \ |
| COND_CODE_1(DT_INST_ON_BUS(inst, i2c), (IST8310_CONFIG_I2C(inst)), \ |
| (IST8310_CONFIG_SPI(inst))) |
| |
| /* |
| * Main instantiation macro, which selects the correct bus-specific |
| * instantiation macros for the instance. |
| */ |
| #define IST8310_DEFINE(inst) \ |
| static struct ist8310_data ist8310_data_##inst; \ |
| static const struct ist8310_config ist8310_config_##inst = {IST8310_BUS_CFG(inst)}; \ |
| \ |
| SENSOR_DEVICE_DT_INST_DEFINE(inst, ist8310_init, NULL, &ist8310_data_##inst, \ |
| &ist8310_config_##inst, POST_KERNEL, \ |
| CONFIG_SENSOR_INIT_PRIORITY, &ist8310_api_funcs); |
| |
| /* Create the struct device for every status "okay" node in the devicetree. */ |
| DT_INST_FOREACH_STATUS_OKAY(IST8310_DEFINE) |