summaryrefslogtreecommitdiffstats
path: root/drivers/iio/magnetometer/ak8974.c
diff options
context:
space:
mode:
authorMichał Mirosław <mirq-linux@rere.qmqm.pl>2017-08-17 15:56:10 +0200
committerJonathan Cameron <Jonathan.Cameron@huawei.com>2017-08-20 12:18:33 +0200
commit21be26fc6786a674b7b10f50ba29e4459cbd1aa8 (patch)
tree49c20ff44b6f9b17cbcc5898075829eac4808d15 /drivers/iio/magnetometer/ak8974.c
parentiio: light: tsl2583: constify i2c_device_id (diff)
downloadlinux-21be26fc6786a674b7b10f50ba29e4459cbd1aa8.tar.xz
linux-21be26fc6786a674b7b10f50ba29e4459cbd1aa8.zip
iio: magnetometer: ak8974: support AMI306 variant
Add support for AMI306 magnetometer - very similar to AMI305. Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl> Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Diffstat (limited to 'drivers/iio/magnetometer/ak8974.c')
-rw-r--r--drivers/iio/magnetometer/ak8974.c90
1 files changed, 70 insertions, 20 deletions
diff --git a/drivers/iio/magnetometer/ak8974.c b/drivers/iio/magnetometer/ak8974.c
index e13370dc9b1c..76091da20a0c 100644
--- a/drivers/iio/magnetometer/ak8974.c
+++ b/drivers/iio/magnetometer/ak8974.c
@@ -36,7 +36,7 @@
* and MSB is at the next higher address.
*/
-/* These registers are common for AK8974 and AMI305 */
+/* These registers are common for AK8974 and AMI30x */
#define AK8974_SELFTEST 0x0C
#define AK8974_SELFTEST_IDLE 0x55
#define AK8974_SELFTEST_OK 0xAA
@@ -44,6 +44,7 @@
#define AK8974_INFO 0x0D
#define AK8974_WHOAMI 0x0F
+#define AK8974_WHOAMI_VALUE_AMI306 0x46
#define AK8974_WHOAMI_VALUE_AMI305 0x47
#define AK8974_WHOAMI_VALUE_AK8974 0x48
@@ -73,6 +74,35 @@
#define AK8974_TEMP 0x31
#define AMI305_TEMP 0x60
+/* AMI306-specific control register */
+#define AMI306_CTRL4 0x5C
+
+/* AMI306 factory calibration data */
+
+/* fine axis sensitivity */
+#define AMI306_FINEOUTPUT_X 0x90
+#define AMI306_FINEOUTPUT_Y 0x92
+#define AMI306_FINEOUTPUT_Z 0x94
+
+/* axis sensitivity */
+#define AMI306_SENS_X 0x96
+#define AMI306_SENS_Y 0x98
+#define AMI306_SENS_Z 0x9A
+
+/* axis cross-interference */
+#define AMI306_GAIN_PARA_XZ 0x9C
+#define AMI306_GAIN_PARA_XY 0x9D
+#define AMI306_GAIN_PARA_YZ 0x9E
+#define AMI306_GAIN_PARA_YX 0x9F
+#define AMI306_GAIN_PARA_ZY 0xA0
+#define AMI306_GAIN_PARA_ZX 0xA1
+
+/* offset at ZERO magnetic field */
+#define AMI306_OFFZERO_X 0xF8
+#define AMI306_OFFZERO_Y 0xFA
+#define AMI306_OFFZERO_Z 0xFC
+
+
#define AK8974_INT_X_HIGH BIT(7) /* Axis over +threshold */
#define AK8974_INT_Y_HIGH BIT(6)
#define AK8974_INT_Z_HIGH BIT(5)
@@ -158,6 +188,26 @@ struct ak8974 {
static const char ak8974_reg_avdd[] = "avdd";
static const char ak8974_reg_dvdd[] = "dvdd";
+static int ak8974_get_u16_val(struct ak8974 *ak8974, u8 reg, u16 *val)
+{
+ int ret;
+ __le16 bulk;
+
+ ret = regmap_bulk_read(ak8974->map, reg, &bulk, 2);
+ if (ret)
+ return ret;
+ *val = le16_to_cpu(bulk);
+
+ return 0;
+}
+
+static int ak8974_set_u16_val(struct ak8974 *ak8974, u8 reg, u16 val)
+{
+ __le16 bulk = cpu_to_le16(val);
+
+ return regmap_bulk_write(ak8974->map, reg, &bulk, 2);
+}
+
static int ak8974_set_power(struct ak8974 *ak8974, bool mode)
{
int ret;
@@ -209,6 +259,12 @@ static int ak8974_configure(struct ak8974 *ak8974)
ret = regmap_write(ak8974->map, AK8974_CTRL3, 0);
if (ret)
return ret;
+ if (ak8974->variant == AK8974_WHOAMI_VALUE_AMI306) {
+ /* magic from datasheet: set high-speed measurement mode */
+ ret = ak8974_set_u16_val(ak8974, AMI306_CTRL4, 0xA07E);
+ if (ret)
+ return ret;
+ }
ret = regmap_write(ak8974->map, AK8974_INT_CTRL, AK8974_INT_CTRL_POL);
if (ret)
return ret;
@@ -388,19 +444,6 @@ static int ak8974_selftest(struct ak8974 *ak8974)
return 0;
}
-static int ak8974_get_u16_val(struct ak8974 *ak8974, u8 reg, u16 *val)
-{
- int ret;
- __le16 bulk;
-
- ret = regmap_bulk_read(ak8974->map, reg, &bulk, 2);
- if (ret)
- return ret;
- *val = le16_to_cpu(bulk);
-
- return 0;
-}
-
static int ak8974_detect(struct ak8974 *ak8974)
{
unsigned int whoami;
@@ -413,9 +456,13 @@ static int ak8974_detect(struct ak8974 *ak8974)
if (ret)
return ret;
+ name = "ami305";
+
switch (whoami) {
+ case AK8974_WHOAMI_VALUE_AMI306:
+ name = "ami306";
+ /* fall-through */
case AK8974_WHOAMI_VALUE_AMI305:
- name = "ami305";
ret = regmap_read(ak8974->map, AMI305_VER, &fw);
if (ret)
return ret;
@@ -602,9 +649,11 @@ static bool ak8974_writeable_reg(struct device *dev, unsigned int reg)
case AMI305_OFFSET_Y + 1:
case AMI305_OFFSET_Z:
case AMI305_OFFSET_Z + 1:
- if (ak8974->variant == AK8974_WHOAMI_VALUE_AMI305)
- return true;
- return false;
+ return ak8974->variant == AK8974_WHOAMI_VALUE_AMI305 ||
+ ak8974->variant == AK8974_WHOAMI_VALUE_AMI306;
+ case AMI306_CTRL4:
+ case AMI306_CTRL4 + 1:
+ return ak8974->variant == AK8974_WHOAMI_VALUE_AMI306;
default:
return false;
}
@@ -678,7 +727,7 @@ static int ak8974_probe(struct i2c_client *i2c,
ret = ak8974_detect(ak8974);
if (ret) {
- dev_err(&i2c->dev, "neither AK8974 nor AMI305 found\n");
+ dev_err(&i2c->dev, "neither AK8974 nor AMI30x found\n");
goto power_off;
}
@@ -827,6 +876,7 @@ static const struct dev_pm_ops ak8974_dev_pm_ops = {
static const struct i2c_device_id ak8974_id[] = {
{"ami305", 0 },
+ {"ami306", 0 },
{"ak8974", 0 },
{}
};
@@ -850,7 +900,7 @@ static struct i2c_driver ak8974_driver = {
};
module_i2c_driver(ak8974_driver);
-MODULE_DESCRIPTION("AK8974 and AMI305 3-axis magnetometer driver");
+MODULE_DESCRIPTION("AK8974 and AMI30x 3-axis magnetometer driver");
MODULE_AUTHOR("Samu Onkalo");
MODULE_AUTHOR("Linus Walleij");
MODULE_LICENSE("GPL v2");