From 94558c2254c47cb782421ab7f27c37ca3899f8f5 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 18 Mar 2021 20:19:25 +0800 Subject: [PATCH 392/464] iio: magnetometer: add a driver for Voltafield AF8133J magnetometer AF8133J is a simple I2C-connected magnetometer, without interrupts. Add a simple IIO driver for it. Signed-off-by: Icenowy Zheng Signed-off-by: Dalton Durst Signed-off-by: Shoji Keita --- drivers/iio/magnetometer/Kconfig | 12 + drivers/iio/magnetometer/Makefile | 1 + drivers/iio/magnetometer/af8133j.c | 351 +++++++++++++++++++++++++++++ 3 files changed, 364 insertions(+) create mode 100644 drivers/iio/magnetometer/af8133j.c diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index 38532d840f2a..cd2917d71904 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -6,6 +6,18 @@ menu "Magnetometer sensors" +config AF8133J + tristate "Voltafield AF8133J 3-Axis Magnetometer" + depends on I2C + depends on OF + select REGMAP_I2C + help + Say yes here to build support for Voltafield AF8133J I2C-based + 3-axis magnetometer chip. + + To compile this driver as a module, choose M here: the module + will be called af8133j. + config AK8974 tristate "Asahi Kasei AK8974 3-Axis Magnetometer" depends on I2C diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index b1c784ea71c8..ec5c46fbf999 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -4,6 +4,7 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_AF8133J) += af8133j.o obj-$(CONFIG_AK8974) += ak8974.o obj-$(CONFIG_AK8975) += ak8975.o obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o diff --git a/drivers/iio/magnetometer/af8133j.c b/drivers/iio/magnetometer/af8133j.c new file mode 100644 index 000000000000..f71b413ca3c9 --- /dev/null +++ b/drivers/iio/magnetometer/af8133j.c @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * af8133j.c - Voltafield AF8133J magnetometer driver + * + * Based on mmc35240.c, which is: + * Copyright (c) 2015, Intel Corporation. + * + * TODO: + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define AF8133J_DRV_NAME "af8133j" +#define AF8133J_REGMAP_NAME "af8133j_regmap" + +#define AF8133J_REG_OUT 0x03 +/* Little endian */ +#define AF8133J_REG_OUT_SIZE 0x06 + +#define AF8133J_REG_PCODE 0x00 +#define AF8133J_REG_PCODE_VAL 0x5e + +/* Named STATUS in datasheet, renamed here to prevent confusion with STATE */ +#define AF8133J_REG_DRDY 0x02 +#define AF8133J_REG_DRDY_ACQ BIT(0) +/* Named STATE in datasheet, renamed here to prevent confusion */ +#define AF8133J_REG_STATE 0x0a +#define AF8133J_REG_STATE_STBY 0x00 +#define AF8133J_REG_STATE_WORK 0x01 +#define AF8133J_REG_RANGE 0x0b +#define AF8133J_REG_RANGE_22G 0x12 +#define AF8133J_REG_RANGE_12G 0x34 +/* Software reset */ +#define AF8133J_REG_SWR 0x11 +#define AF8133J_REG_SWR_PERFORM BIT(0) + +struct af8133j_data { + struct i2c_client *client; + struct regmap *regmap; + struct mutex mutex; + + struct gpio_desc *reset_gpiod; + struct regulator *avdd; + struct regulator *dvdd; + struct iio_mount_matrix orientation; +}; + +enum af8133j_axis { + AXIS_X = 0, + AXIS_Y, + AXIS_Z, +}; + +static struct iio_mount_matrix * +af8133j_get_mount_matrix(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct af8133j_data *data = iio_priv(indio_dev); + return &data->orientation; +} + +static const struct iio_chan_spec_ext_info af8133j_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, af8133j_get_mount_matrix), + { } +}; + +#define AF8133J_CHANNEL(_axis) { \ + .type = IIO_MAGN, \ + .modified = 1, \ + .channel2 = IIO_MOD_ ## _axis, \ + .address = AXIS_ ## _axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .ext_info = af8133j_ext_info, \ +} + +static const struct iio_chan_spec af8133j_channels[] = { + AF8133J_CHANNEL(X), + AF8133J_CHANNEL(Y), + AF8133J_CHANNEL(Z), +}; + +static int af8133j_init(struct af8133j_data *data) +{ + unsigned int val; + int ret; + + ret = regmap_read(data->regmap, AF8133J_REG_PCODE, &val); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading product code\n"); + return ret; + } + + if (val != AF8133J_REG_PCODE_VAL) { + dev_warn(&data->client->dev, + "Unknown AF8133J product code %x\n", val); + } + + /* Reset the chip */ + ret = regmap_write(data->regmap, AF8133J_REG_SWR, + AF8133J_REG_SWR_PERFORM); + if (ret < 0) { + dev_err(&data->client->dev, "Failed to write regmap\n"); + return ret; + } + + /* Wait for reset finish */ + usleep_range(1000, 1100); + + /* Check whether the reset bit is cleared */ + ret = regmap_read(data->regmap, AF8133J_REG_SWR, &val); + if (ret < 0) { + dev_err(&data->client->dev, "Failed to read regmap\n"); + return ret; + } + if (val & AF8133J_REG_SWR_PERFORM) { + dev_err(&data->client->dev, "Device is not responding\n"); + return -EIO; + } + + /* The reset value should satisfy us now. */ + + return 0; +} + +static int af8133j_take_measurement(struct af8133j_data *data) +{ + unsigned int val; + int ret; + + ret = regmap_write(data->regmap, AF8133J_REG_STATE, + AF8133J_REG_STATE_WORK); + if (ret < 0) + return ret; + + /* The datasheet says "Mesaure Time <1.5ms" */ + ret = regmap_read_poll_timeout(data->regmap, AF8133J_REG_DRDY, val, + val & AF8133J_REG_DRDY_ACQ, + 100, 1500); + if (ret < 0) + return ret; + + ret = regmap_write(data->regmap, AF8133J_REG_STATE, + AF8133J_REG_STATE_STBY); + if (ret < 0) + return ret; + + return 0; +} + +static int af8133j_read_measurement(struct af8133j_data *data, __le16 buf[3]) +{ + int ret; + + ret = af8133j_take_measurement(data); + if (ret < 0) + return ret; + + return regmap_bulk_read(data->regmap, AF8133J_REG_OUT, buf, + 3 * sizeof(__le16)); +} + +static int af8133j_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct af8133j_data *data = iio_priv(indio_dev); + int ret; + __le16 buf[3]; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&data->mutex); + ret = af8133j_read_measurement(data, buf); + mutex_unlock(&data->mutex); + if (ret < 0) + return ret; + *val = sign_extend32(le16_to_cpu(buf[chan->address]), 15); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* We only use the default 12G scale now */ + *val = 0; + *val2 = 12 * 1000000 / 32768; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static const struct iio_info af8133j_info = { + .read_raw = af8133j_read_raw, +}; + +static const struct regmap_config af8133j_regmap_config = { + .name = AF8133J_REGMAP_NAME, + + .reg_bits = 8, + .val_bits = 8, + + .max_register = AF8133J_REG_SWR, + .cache_type = REGCACHE_NONE, +}; + +static int af8133j_probe(struct i2c_client *client) +{ + struct af8133j_data *data; + struct iio_dev *indio_dev; + struct regmap *regmap; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + regmap = devm_regmap_init_i2c(client, &af8133j_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "regmap initialization failed\n"); + return PTR_ERR(regmap); + } + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + data->regmap = regmap; + + data->reset_gpiod = devm_gpiod_get(&client->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(data->reset_gpiod)) { + dev_err(&client->dev, "Got error while retrieving reset gpio\n"); + ret = PTR_ERR(data->reset_gpiod); + goto out_release_reset_gpio; + } + + data->avdd = devm_regulator_get(&client->dev, "avdd"); + if (IS_ERR(data->avdd)) { + dev_err(&client->dev, "Got error while retrieving avdd reg\n"); + ret = PTR_ERR(data->avdd); + goto out_release_reset_gpio; + } + + data->dvdd = devm_regulator_get(&client->dev, "dvdd"); + if (IS_ERR(data->dvdd)) { + dev_err(&client->dev, "Got error while retrieving dvdd reg\n"); + ret = PTR_ERR(data->dvdd); + goto out_release_reset_gpio; + } + + mutex_init(&data->mutex); + + ret = iio_read_mount_matrix(&client->dev, "mount-matrix", + &data->orientation); + if (ret) + return ret; + + indio_dev->info = &af8133j_info; + indio_dev->name = AF8133J_DRV_NAME; + indio_dev->channels = af8133j_channels; + indio_dev->num_channels = ARRAY_SIZE(af8133j_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + /* Power sequence */ + ret = regulator_enable(data->avdd); + if (ret) { + dev_err(&client->dev, "Failed to enable avdd regulator"); + goto out_release_reset_gpio; + } + + ret = regulator_enable(data->dvdd); + if (ret) { + dev_err(&client->dev, "Failed to enable dvdd regulator"); + goto out_disable_avdd; + } + + msleep(15); + + gpiod_set_value_cansleep(data->reset_gpiod, 0); + + msleep(1); + + ret = af8133j_init(data); + if (ret) { + dev_err(&client->dev, "Failed to init af8133j"); + goto out_assert_reset; + } + + ret = devm_iio_device_register(&client->dev, indio_dev); + if (ret) { + dev_err(&client->dev, "Failed to register iio device"); + goto out_assert_reset; + } + + return 0; + +out_assert_reset: + gpiod_set_value_cansleep(data->reset_gpiod, 1); + regulator_disable(data->dvdd); +out_disable_avdd: + regulator_disable(data->avdd); +out_release_reset_gpio: + gpiod_direction_input(data->reset_gpiod); + + return ret; +} + +static int af8133j_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct af8133j_data *data = iio_priv(indio_dev); + + gpiod_set_value_cansleep(data->reset_gpiod, 0); + regulator_disable(data->dvdd); + regulator_disable(data->avdd); + + return 0; +} + +static const struct of_device_id af8133j_of_match[] = { + { .compatible = "voltafield,af8133j", }, + { } +}; +MODULE_DEVICE_TABLE(of, af8133j_of_match); + +static const struct i2c_device_id af8133j_id[] = { + {"af8133j", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, af8133j_id); + +static struct i2c_driver af8133j_driver = { + .driver = { + .name = AF8133J_DRV_NAME, + .of_match_table = af8133j_of_match, + }, + .probe = af8133j_probe, + .remove = af8133j_remove, + .id_table = af8133j_id, +}; + +module_i2c_driver(af8133j_driver); + +MODULE_AUTHOR("Icenowy Zheng "); +MODULE_DESCRIPTION("Voltafield AF8133J magnetic sensor driver"); +MODULE_LICENSE("GPL v2"); -- 2.34.1