// SPDX-License-Identifier: GPL-2.0-only /* * Driver for the Allegro MicroSystems ALS31300 3-D Linear Hall Effect Sensor * * Copyright (c) 2024 Linaro Limited */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * The Allegro MicroSystems ALS31300 has an EEPROM space to configure how * the device works and how the interrupt line behaves. * Only the default setup with external trigger is supported. * * While the bindings supports declaring an interrupt line, those * events are not supported. * * It should be possible to adapt the driver to the current * device EEPROM setup at runtime. */ #define ALS31300_EEPROM_CONFIG 0x02 #define ALS31300_EEPROM_INTERRUPT 0x03 #define ALS31300_EEPROM_CUSTOMER_1 0x0d #define ALS31300_EEPROM_CUSTOMER_2 0x0e #define ALS31300_EEPROM_CUSTOMER_3 0x0f #define ALS31300_VOL_MODE 0x27 #define ALS31300_VOL_MODE_LPDCM GENMASK(6, 4) #define ALS31300_LPDCM_INACTIVE_0_5_MS 0 #define ALS31300_LPDCM_INACTIVE_1_0_MS 1 #define ALS31300_LPDCM_INACTIVE_5_0_MS 2 #define ALS31300_LPDCM_INACTIVE_10_0_MS 3 #define ALS31300_LPDCM_INACTIVE_50_0_MS 4 #define ALS31300_LPDCM_INACTIVE_100_0_MS 5 #define ALS31300_LPDCM_INACTIVE_500_0_MS 6 #define ALS31300_LPDCM_INACTIVE_1000_0_MS 7 #define ALS31300_VOL_MODE_SLEEP GENMASK(1, 0) #define ALS31300_VOL_MODE_ACTIVE_MODE 0 #define ALS31300_VOL_MODE_SLEEP_MODE 1 #define ALS31300_VOL_MODE_LPDCM_MODE 2 #define ALS31300_VOL_MSB 0x28 #define ALS31300_VOL_MSB_TEMPERATURE GENMASK(5, 0) #define ALS31300_VOL_MSB_INTERRUPT BIT(6) #define ALS31300_VOL_MSB_NEW_DATA BIT(7) #define ALS31300_VOL_MSB_Z_AXIS GENMASK(15, 8) #define ALS31300_VOL_MSB_Y_AXIS GENMASK(23, 16) #define ALS31300_VOL_MSB_X_AXIS GENMASK(31, 24) #define ALS31300_VOL_LSB 0x29 #define ALS31300_VOL_LSB_TEMPERATURE GENMASK(5, 0) #define ALS31300_VOL_LSB_HALL_STATUS GENMASK(7, 7) #define ALS31300_VOL_LSB_Z_AXIS GENMASK(11, 8) #define ALS31300_VOL_LSB_Y_AXIS GENMASK(15, 12) #define ALS31300_VOL_LSB_X_AXIS GENMASK(19, 16) #define ALS31300_VOL_LSB_INTERRUPT_WRITE BIT(20) #define ALS31300_CUSTOMER_ACCESS 0x35 #define ALS31300_DATA_X_GET(b) \ sign_extend32(FIELD_GET(ALS31300_VOL_MSB_X_AXIS, b[0]) << 4 | \ FIELD_GET(ALS31300_VOL_LSB_X_AXIS, b[1]), 11) #define ALS31300_DATA_Y_GET(b) \ sign_extend32(FIELD_GET(ALS31300_VOL_MSB_Y_AXIS, b[0]) << 4 | \ FIELD_GET(ALS31300_VOL_LSB_Y_AXIS, b[1]), 11) #define ALS31300_DATA_Z_GET(b) \ sign_extend32(FIELD_GET(ALS31300_VOL_MSB_Z_AXIS, b[0]) << 4 | \ FIELD_GET(ALS31300_VOL_LSB_Z_AXIS, b[1]), 11) #define ALS31300_TEMPERATURE_GET(b) \ (FIELD_GET(ALS31300_VOL_MSB_TEMPERATURE, b[0]) << 6 | \ FIELD_GET(ALS31300_VOL_LSB_TEMPERATURE, b[1])) enum als31300_channels { TEMPERATURE = 0, AXIS_X, AXIS_Y, AXIS_Z, }; struct als31300_variant_info { u8 sensitivity; }; struct als31300_data { struct device *dev; /* protects power on/off the device and access HW */ struct mutex mutex; const struct als31300_variant_info *variant_info; struct regmap *map; }; /* The whole measure is split into 2x32-bit registers, we need to read them both at once */ static int als31300_get_measure(struct als31300_data *data, u16 *t, s16 *x, s16 *y, s16 *z) { u32 buf[2]; int ret, err; guard(mutex)(&data->mutex); ret = pm_runtime_resume_and_get(data->dev); if (ret) return ret; /* * Loop until data is valid, new data should have the * ALS31300_VOL_MSB_NEW_DATA bit set to 1. * Max update rate is 2KHz, wait up to 1ms. */ ret = read_poll_timeout(regmap_bulk_read, err, err || FIELD_GET(ALS31300_VOL_MSB_NEW_DATA, buf[0]), 20, USEC_PER_MSEC, false, data->map, ALS31300_VOL_MSB, buf, ARRAY_SIZE(buf)); /* Bail out on read_poll_timeout() error */ if (ret) goto out; /* Bail out on regmap_bulk_read() error */ if (err) { dev_err(data->dev, "read data failed, error %d\n", ret); ret = err; goto out; } *t = ALS31300_TEMPERATURE_GET(buf); *x = ALS31300_DATA_X_GET(buf); *y = ALS31300_DATA_Y_GET(buf); *z = ALS31300_DATA_Z_GET(buf); out: pm_runtime_mark_last_busy(data->dev); pm_runtime_put_autosuspend(data->dev); return ret; } static int als31300_read_raw(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, int *val, int *val2, long mask) { struct als31300_data *data = iio_priv(indio_dev); s16 x, y, z; u16 t; int ret; switch (mask) { case IIO_CHAN_INFO_PROCESSED: case IIO_CHAN_INFO_RAW: ret = als31300_get_measure(data, &t, &x, &y, &z); if (ret) return ret; switch (chan->address) { case TEMPERATURE: *val = t; return IIO_VAL_INT; case AXIS_X: *val = x; return IIO_VAL_INT; case AXIS_Y: *val = y; return IIO_VAL_INT; case AXIS_Z: *val = z; return IIO_VAL_INT; default: return -EINVAL; } case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_TEMP: /* * Fractional part of: * 1000 * 302 * (value - 1708) * temp = ---------------------------- * 4096 * to convert temperature in millicelcius. */ *val = MILLI * 302; *val2 = 4096; return IIO_VAL_FRACTIONAL; case IIO_MAGN: /* * Devices are configured in factory * with different sensitivities: * - 500 GAUSS <-> 4 LSB/Gauss * - 1000 GAUSS <-> 2 LSB/Gauss * - 2000 GAUSS <-> 1 LSB/Gauss * with translates by a division of the returned * value to get Gauss value. * The sensitivity cannot be read at runtime * so the value depends on the model compatible * or device id. */ *val = 1; *val2 = data->variant_info->sensitivity; return IIO_VAL_FRACTIONAL; default: return -EINVAL; } case IIO_CHAN_INFO_OFFSET: switch (chan->type) { case IIO_TEMP: *val = -1708; return IIO_VAL_INT; default: return -EINVAL; } default: return -EINVAL; } } static irqreturn_t als31300_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct als31300_data *data = iio_priv(indio_dev); struct { u16 temperature; s16 channels[3]; aligned_s64 timestamp; } scan; s16 x, y, z; int ret; u16 t; ret = als31300_get_measure(data, &t, &x, &y, &z); if (ret) goto trigger_out; scan.temperature = t; scan.channels[0] = x; scan.channels[1] = y; scan.channels[2] = z; iio_push_to_buffers_with_timestamp(indio_dev, &scan, pf->timestamp); trigger_out: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; } #define ALS31300_AXIS_CHANNEL(axis, index) \ { \ .type = IIO_MAGN, \ .modified = 1, \ .channel2 = IIO_MOD_##axis, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_SCALE), \ .address = index, \ .scan_index = index, \ .scan_type = { \ .sign = 's', \ .realbits = 12, \ .storagebits = 16, \ .endianness = IIO_CPU, \ }, \ } static const struct iio_chan_spec als31300_channels[] = { { .type = IIO_TEMP, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), .address = TEMPERATURE, .scan_index = TEMPERATURE, .scan_type = { .sign = 'u', .realbits = 16, .storagebits = 16, .endianness = IIO_CPU, }, }, ALS31300_AXIS_CHANNEL(X, AXIS_X), ALS31300_AXIS_CHANNEL(Y, AXIS_Y), ALS31300_AXIS_CHANNEL(Z, AXIS_Z), IIO_CHAN_SOFT_TIMESTAMP(4), }; static const struct iio_info als31300_info = { .read_raw = als31300_read_raw, }; static int als31300_set_operating_mode(struct als31300_data *data, unsigned int val) { int ret; ret = regmap_update_bits(data->map, ALS31300_VOL_MODE, ALS31300_VOL_MODE_SLEEP, val); if (ret) { dev_err(data->dev, "failed to set operating mode (%pe)\n", ERR_PTR(ret)); return ret; } /* The time it takes to exit sleep mode is equivalent to Power-On Delay Time */ if (val == ALS31300_VOL_MODE_ACTIVE_MODE) fsleep(600); return 0; } static void als31300_power_down(void *data) { als31300_set_operating_mode(data, ALS31300_VOL_MODE_SLEEP_MODE); } static const struct iio_buffer_setup_ops als31300_setup_ops = {}; static const unsigned long als31300_scan_masks[] = { GENMASK(3, 0), 0 }; static bool als31300_volatile_reg(struct device *dev, unsigned int reg) { return reg == ALS31300_VOL_MSB || reg == ALS31300_VOL_LSB; } static const struct regmap_config als31300_regmap_config = { .reg_bits = 8, .val_bits = 32, .max_register = ALS31300_CUSTOMER_ACCESS, .volatile_reg = als31300_volatile_reg, }; static int als31300_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; struct als31300_data *data; struct iio_dev *indio_dev; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; data = iio_priv(indio_dev); data->dev = dev; i2c_set_clientdata(i2c, indio_dev); ret = devm_mutex_init(dev, &data->mutex); if (ret) return ret; data->variant_info = i2c_get_match_data(i2c); if (!data->variant_info) return -EINVAL; data->map = devm_regmap_init_i2c(i2c, &als31300_regmap_config); if (IS_ERR(data->map)) return dev_err_probe(dev, PTR_ERR(data->map), "failed to allocate register map\n"); ret = devm_regulator_get_enable(dev, "vcc"); if (ret) return dev_err_probe(dev, ret, "failed to enable regulator\n"); ret = als31300_set_operating_mode(data, ALS31300_VOL_MODE_ACTIVE_MODE); if (ret) return dev_err_probe(dev, ret, "failed to power on device\n"); ret = devm_add_action_or_reset(dev, als31300_power_down, data); if (ret) return dev_err_probe(dev, ret, "failed to add powerdown action\n"); indio_dev->info = &als31300_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->name = i2c->name; indio_dev->channels = als31300_channels; indio_dev->num_channels = ARRAY_SIZE(als31300_channels); indio_dev->available_scan_masks = als31300_scan_masks; ret = devm_iio_triggered_buffer_setup(dev, indio_dev, iio_pollfunc_store_time, als31300_trigger_handler, &als31300_setup_ops); if (ret < 0) return dev_err_probe(dev, ret, "iio triggered buffer setup failed\n"); ret = pm_runtime_set_active(dev); if (ret < 0) return ret; ret = devm_pm_runtime_enable(dev); if (ret) return ret; pm_runtime_get_noresume(dev); pm_runtime_set_autosuspend_delay(dev, 200); pm_runtime_use_autosuspend(dev); pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); ret = devm_iio_device_register(dev, indio_dev); if (ret) return dev_err_probe(dev, ret, "device register failed\n"); return 0; } static int als31300_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct als31300_data *data = iio_priv(indio_dev); return als31300_set_operating_mode(data, ALS31300_VOL_MODE_SLEEP_MODE); } static int als31300_runtime_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct als31300_data *data = iio_priv(indio_dev); return als31300_set_operating_mode(data, ALS31300_VOL_MODE_ACTIVE_MODE); } static DEFINE_RUNTIME_DEV_PM_OPS(als31300_pm_ops, als31300_runtime_suspend, als31300_runtime_resume, NULL); static const struct als31300_variant_info al31300_variant_500 = { .sensitivity = 4, }; static const struct als31300_variant_info al31300_variant_1000 = { .sensitivity = 2, }; static const struct als31300_variant_info al31300_variant_2000 = { .sensitivity = 1, }; static const struct i2c_device_id als31300_id[] = { { .name = "als31300-500", .driver_data = (kernel_ulong_t)&al31300_variant_500, }, { .name = "als31300-1000", .driver_data = (kernel_ulong_t)&al31300_variant_1000, }, { .name = "als31300-2000", .driver_data = (kernel_ulong_t)&al31300_variant_2000, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, als31300_id); static const struct of_device_id als31300_of_match[] = { { .compatible = "allegromicro,als31300-500", .data = &al31300_variant_500, }, { .compatible = "allegromicro,als31300-1000", .data = &al31300_variant_1000, }, { .compatible = "allegromicro,als31300-2000", .data = &al31300_variant_2000, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, als31300_of_match); static struct i2c_driver als31300_driver = { .driver = { .name = "als31300", .of_match_table = als31300_of_match, .pm = pm_ptr(&als31300_pm_ops), }, .probe = als31300_probe, .id_table = als31300_id, }; module_i2c_driver(als31300_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("ALS31300 3-D Linear Hall Effect Driver"); MODULE_AUTHOR("Neil Armstrong ");