From 072de836f53be56a70cecf70b43ae43b7ce17376 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Mon, 11 Dec 2023 10:08:36 +0000
Subject: [PATCH] mk-rootfs.sh
---
kernel/drivers/iio/magnetometer/ak8974.c | 213 ++++++++++++++++++++++++++++++++++++++++-------------
1 files changed, 160 insertions(+), 53 deletions(-)
diff --git a/kernel/drivers/iio/magnetometer/ak8974.c b/kernel/drivers/iio/magnetometer/ak8974.c
index d3f0940..24b2f7b 100644
--- a/kernel/drivers/iio/magnetometer/ak8974.c
+++ b/kernel/drivers/iio/magnetometer/ak8974.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the Asahi Kasei EMD Corporation AK8974
* and Aichi Steel AMI305 magnetometer chips.
@@ -11,6 +12,7 @@
* Author: Linus Walleij <linus.walleij@linaro.org>
*/
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
@@ -48,6 +50,7 @@
#define AK8974_WHOAMI_VALUE_AMI306 0x46
#define AK8974_WHOAMI_VALUE_AMI305 0x47
#define AK8974_WHOAMI_VALUE_AK8974 0x48
+#define AK8974_WHOAMI_VALUE_HSCDTD008A 0x49
#define AK8974_DATA_X 0x10
#define AK8974_DATA_Y 0x12
@@ -139,6 +142,12 @@
#define AK8974_INT_CTRL_PULSE BIT(1) /* 0 = latched; 1 = pulse (50 usec) */
#define AK8974_INT_CTRL_RESDEF (AK8974_INT_CTRL_XYZEN | AK8974_INT_CTRL_POL)
+/* HSCDTD008A-specific control register */
+#define HSCDTD008A_CTRL4 0x1E
+#define HSCDTD008A_CTRL4_MMD BIT(7) /* must be set to 1 */
+#define HSCDTD008A_CTRL4_RANGE BIT(4) /* 0 = 14-bit output; 1 = 15-bit output */
+#define HSCDTD008A_CTRL4_RESDEF (HSCDTD008A_CTRL4_MMD | HSCDTD008A_CTRL4_RANGE)
+
/* The AMI305 has elaborate FW version and serial number registers */
#define AMI305_VER 0xE8
#define AMI305_SN 0xEA
@@ -172,6 +181,7 @@
* @drdy_irq: uses the DRDY IRQ line
* @drdy_complete: completion for DRDY
* @drdy_active_low: the DRDY IRQ is active low
+ * @scan: timestamps
*/
struct ak8974 {
struct i2c_client *i2c;
@@ -245,10 +255,17 @@
ret = regmap_write(ak8974->map, AK8974_CTRL3, AK8974_CTRL3_RESDEF);
if (ret)
return ret;
- ret = regmap_write(ak8974->map, AK8974_INT_CTRL,
- AK8974_INT_CTRL_RESDEF);
- if (ret)
- return ret;
+ if (ak8974->variant != AK8974_WHOAMI_VALUE_HSCDTD008A) {
+ ret = regmap_write(ak8974->map, AK8974_INT_CTRL,
+ AK8974_INT_CTRL_RESDEF);
+ if (ret)
+ return ret;
+ } else {
+ ret = regmap_write(ak8974->map, HSCDTD008A_CTRL4,
+ HSCDTD008A_CTRL4_RESDEF);
+ if (ret)
+ return ret;
+ }
/* After reset, power off is default state */
return ak8974_set_power(ak8974, AK8974_PWR_OFF);
@@ -271,6 +288,8 @@
if (ret)
return ret;
}
+ if (ak8974->variant == AK8974_WHOAMI_VALUE_HSCDTD008A)
+ return 0;
ret = regmap_write(ak8974->map, AK8974_INT_CTRL, AK8974_INT_CTRL_POL);
if (ret)
return ret;
@@ -481,7 +500,7 @@
switch (whoami) {
case AK8974_WHOAMI_VALUE_AMI306:
name = "ami306";
- /* fall-through */
+ fallthrough;
case AK8974_WHOAMI_VALUE_AMI305:
ret = regmap_read(ak8974->map, AMI305_VER, &fw);
if (ret)
@@ -498,6 +517,10 @@
case AK8974_WHOAMI_VALUE_AK8974:
name = "ak8974";
dev_info(&ak8974->i2c->dev, "detected AK8974\n");
+ break;
+ case AK8974_WHOAMI_VALUE_HSCDTD008A:
+ name = "hscdtd008a";
+ dev_info(&ak8974->i2c->dev, "detected hscdtd008a\n");
break;
default:
dev_err(&ak8974->i2c->dev, "unsupported device (%02x) ",
@@ -538,47 +561,103 @@
return 0;
}
+static int ak8974_measure_channel(struct ak8974 *ak8974, unsigned long address,
+ int *val)
+{
+ __le16 hw_values[3];
+ int ret;
+
+ pm_runtime_get_sync(&ak8974->i2c->dev);
+ mutex_lock(&ak8974->lock);
+
+ /*
+ * We read all axes and discard all but one, for optimized
+ * reading, use the triggered buffer.
+ */
+ ret = ak8974_trigmeas(ak8974);
+ if (ret)
+ goto out_unlock;
+ ret = ak8974_getresult(ak8974, hw_values);
+ if (ret)
+ goto out_unlock;
+ /*
+ * This explicit cast to (s16) is necessary as the measurement
+ * is done in 2's complement with positive and negative values.
+ * The follwing assignment to *val will then convert the signed
+ * s16 value to a signed int value.
+ */
+ *val = (s16)le16_to_cpu(hw_values[address]);
+out_unlock:
+ mutex_unlock(&ak8974->lock);
+ pm_runtime_mark_last_busy(&ak8974->i2c->dev);
+ pm_runtime_put_autosuspend(&ak8974->i2c->dev);
+
+ return ret;
+}
+
static int ak8974_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2,
long mask)
{
struct ak8974 *ak8974 = iio_priv(indio_dev);
- __le16 hw_values[3];
- int ret = -EINVAL;
-
- pm_runtime_get_sync(&ak8974->i2c->dev);
- mutex_lock(&ak8974->lock);
+ int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (chan->address > 2) {
dev_err(&ak8974->i2c->dev, "faulty channel address\n");
- ret = -EIO;
- goto out_unlock;
+ return -EIO;
}
- ret = ak8974_trigmeas(ak8974);
+ ret = ak8974_measure_channel(ak8974, chan->address, val);
if (ret)
- goto out_unlock;
- ret = ak8974_getresult(ak8974, hw_values);
- if (ret)
- goto out_unlock;
-
- /*
- * We read all axes and discard all but one, for optimized
- * reading, use the triggered buffer.
- */
- *val = (s16)le16_to_cpu(hw_values[chan->address]);
-
- ret = IIO_VAL_INT;
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ switch (ak8974->variant) {
+ case AK8974_WHOAMI_VALUE_AMI306:
+ case AK8974_WHOAMI_VALUE_AMI305:
+ /*
+ * The datasheet for AMI305 and AMI306, page 6
+ * specifies the range of the sensor to be
+ * +/- 12 Gauss.
+ */
+ *val = 12;
+ /*
+ * 12 bits are used, +/- 2^11
+ * [ -2048 .. 2047 ] (manual page 20)
+ * [ 0xf800 .. 0x07ff ]
+ */
+ *val2 = 11;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case AK8974_WHOAMI_VALUE_HSCDTD008A:
+ /*
+ * The datasheet for HSCDTF008A, page 3 specifies the
+ * range of the sensor as +/- 2.4 mT per axis, which
+ * corresponds to +/- 2400 uT = +/- 24 Gauss.
+ */
+ *val = 24;
+ /*
+ * 15 bits are used (set up in CTRL4), +/- 2^14
+ * [ -16384 .. 16383 ] (manual page 24)
+ * [ 0xc000 .. 0x3fff ]
+ */
+ *val2 = 14;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ /* GUESSING +/- 12 Gauss */
+ *val = 12;
+ /* GUESSING 12 bits ADC +/- 2^11 */
+ *val2 = 11;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+ break;
+ default:
+ /* Unknown request */
+ break;
}
- out_unlock:
- mutex_unlock(&ak8974->lock);
- pm_runtime_mark_last_busy(&ak8974->i2c->dev);
- pm_runtime_put_autosuspend(&ak8974->i2c->dev);
-
- return ret;
+ return -EINVAL;
}
static void ak8974_fill_buffer(struct iio_dev *indio_dev)
@@ -634,27 +713,44 @@
{ },
};
-#define AK8974_AXIS_CHANNEL(axis, index) \
+#define AK8974_AXIS_CHANNEL(axis, index, bits) \
{ \
.type = IIO_MAGN, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
.ext_info = ak8974_ext_info, \
.address = index, \
.scan_index = index, \
.scan_type = { \
.sign = 's', \
- .realbits = 16, \
+ .realbits = bits, \
.storagebits = 16, \
.endianness = IIO_LE \
}, \
}
-static const struct iio_chan_spec ak8974_channels[] = {
- AK8974_AXIS_CHANNEL(X, 0),
- AK8974_AXIS_CHANNEL(Y, 1),
- AK8974_AXIS_CHANNEL(Z, 2),
+/*
+ * We have no datasheet for the AK8974 but we guess that its
+ * ADC is 12 bits. The AMI305 and AMI306 certainly has 12bit
+ * ADC.
+ */
+static const struct iio_chan_spec ak8974_12_bits_channels[] = {
+ AK8974_AXIS_CHANNEL(X, 0, 12),
+ AK8974_AXIS_CHANNEL(Y, 1, 12),
+ AK8974_AXIS_CHANNEL(Z, 2, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+/*
+ * The HSCDTD008A has 15 bits resolution the way we set it up
+ * in CTRL4.
+ */
+static const struct iio_chan_spec ak8974_15_bits_channels[] = {
+ AK8974_AXIS_CHANNEL(X, 0, 15),
+ AK8974_AXIS_CHANNEL(Y, 1, 15),
+ AK8974_AXIS_CHANNEL(Z, 2, 15),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
@@ -677,18 +773,18 @@
case AK8974_INT_CTRL:
case AK8974_INT_THRES:
case AK8974_INT_THRES + 1:
+ return true;
case AK8974_PRESET:
case AK8974_PRESET + 1:
- return true;
+ return ak8974->variant != AK8974_WHOAMI_VALUE_HSCDTD008A;
case AK8974_OFFSET_X:
case AK8974_OFFSET_X + 1:
case AK8974_OFFSET_Y:
case AK8974_OFFSET_Y + 1:
case AK8974_OFFSET_Z:
case AK8974_OFFSET_Z + 1:
- if (ak8974->variant == AK8974_WHOAMI_VALUE_AK8974)
- return true;
- return false;
+ return ak8974->variant == AK8974_WHOAMI_VALUE_AK8974 ||
+ ak8974->variant == AK8974_WHOAMI_VALUE_HSCDTD008A;
case AMI305_OFFSET_X:
case AMI305_OFFSET_X + 1:
case AMI305_OFFSET_Y:
@@ -737,9 +833,8 @@
ak8974->i2c = i2c;
mutex_init(&ak8974->lock);
- ret = of_iio_read_mount_matrix(&i2c->dev,
- "mount-matrix",
- &ak8974->orientation);
+ ret = iio_read_mount_matrix(&i2c->dev, "mount-matrix",
+ &ak8974->orientation);
if (ret)
return ret;
@@ -749,10 +844,8 @@
ret = devm_regulator_bulk_get(&i2c->dev,
ARRAY_SIZE(ak8974->regs),
ak8974->regs);
- if (ret < 0) {
- dev_err(&i2c->dev, "cannot get regulators\n");
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(&i2c->dev, ret, "cannot get regulators\n");
ret = regulator_bulk_enable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
if (ret < 0) {
@@ -795,9 +888,21 @@
goto disable_pm;
}
- indio_dev->dev.parent = &i2c->dev;
- indio_dev->channels = ak8974_channels;
- indio_dev->num_channels = ARRAY_SIZE(ak8974_channels);
+ switch (ak8974->variant) {
+ case AK8974_WHOAMI_VALUE_AMI306:
+ case AK8974_WHOAMI_VALUE_AMI305:
+ indio_dev->channels = ak8974_12_bits_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ak8974_12_bits_channels);
+ break;
+ case AK8974_WHOAMI_VALUE_HSCDTD008A:
+ indio_dev->channels = ak8974_15_bits_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ak8974_15_bits_channels);
+ break;
+ default:
+ indio_dev->channels = ak8974_12_bits_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ak8974_12_bits_channels);
+ break;
+ }
indio_dev->info = &ak8974_info;
indio_dev->available_scan_masks = ak8974_scan_masks;
indio_dev->modes = INDIO_DIRECT_MODE;
@@ -931,12 +1036,14 @@
{"ami305", 0 },
{"ami306", 0 },
{"ak8974", 0 },
+ {"hscdtd008a", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, ak8974_id);
static const struct of_device_id ak8974_of_match[] = {
{ .compatible = "asahi-kasei,ak8974", },
+ { .compatible = "alps,hscdtd008a", },
{}
};
MODULE_DEVICE_TABLE(of, ak8974_of_match);
@@ -945,7 +1052,7 @@
.driver = {
.name = "ak8974",
.pm = &ak8974_dev_pm_ops,
- .of_match_table = of_match_ptr(ak8974_of_match),
+ .of_match_table = ak8974_of_match,
},
.probe = ak8974_probe,
.remove = ak8974_remove,
--
Gitblit v1.6.2