Commit 21be26fc authored by Michał Mirosław's avatar Michał Mirosław Committed by Jonathan Cameron

iio: magnetometer: ak8974: support AMI306 variant

Add support for AMI306 magnetometer - very similar to AMI305.
Signed-off-by: default avatarMichał Mirosław <mirq-linux@rere.qmqm.pl>
Reviewed-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Signed-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent 3282fa3c
...@@ -13,8 +13,8 @@ config AK8974 ...@@ -13,8 +13,8 @@ config AK8974
select IIO_BUFFER select IIO_BUFFER
select IIO_TRIGGERED_BUFFER select IIO_TRIGGERED_BUFFER
help help
Say yes here to build support for Asahi Kasei AK8974 or Say yes here to build support for Asahi Kasei AK8974, AMI305 or
AMI305 I2C-based 3-axis magnetometer chips. AMI306 I2C-based 3-axis magnetometer chips.
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called ak8974. will be called ak8974.
......
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
* and MSB is at the next higher address. * 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 0x0C
#define AK8974_SELFTEST_IDLE 0x55 #define AK8974_SELFTEST_IDLE 0x55
#define AK8974_SELFTEST_OK 0xAA #define AK8974_SELFTEST_OK 0xAA
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#define AK8974_INFO 0x0D #define AK8974_INFO 0x0D
#define AK8974_WHOAMI 0x0F #define AK8974_WHOAMI 0x0F
#define AK8974_WHOAMI_VALUE_AMI306 0x46
#define AK8974_WHOAMI_VALUE_AMI305 0x47 #define AK8974_WHOAMI_VALUE_AMI305 0x47
#define AK8974_WHOAMI_VALUE_AK8974 0x48 #define AK8974_WHOAMI_VALUE_AK8974 0x48
...@@ -73,6 +74,35 @@ ...@@ -73,6 +74,35 @@
#define AK8974_TEMP 0x31 #define AK8974_TEMP 0x31
#define AMI305_TEMP 0x60 #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_X_HIGH BIT(7) /* Axis over +threshold */
#define AK8974_INT_Y_HIGH BIT(6) #define AK8974_INT_Y_HIGH BIT(6)
#define AK8974_INT_Z_HIGH BIT(5) #define AK8974_INT_Z_HIGH BIT(5)
...@@ -158,6 +188,26 @@ struct ak8974 { ...@@ -158,6 +188,26 @@ struct ak8974 {
static const char ak8974_reg_avdd[] = "avdd"; static const char ak8974_reg_avdd[] = "avdd";
static const char ak8974_reg_dvdd[] = "dvdd"; 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) static int ak8974_set_power(struct ak8974 *ak8974, bool mode)
{ {
int ret; int ret;
...@@ -209,6 +259,12 @@ static int ak8974_configure(struct ak8974 *ak8974) ...@@ -209,6 +259,12 @@ static int ak8974_configure(struct ak8974 *ak8974)
ret = regmap_write(ak8974->map, AK8974_CTRL3, 0); ret = regmap_write(ak8974->map, AK8974_CTRL3, 0);
if (ret) if (ret)
return 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); ret = regmap_write(ak8974->map, AK8974_INT_CTRL, AK8974_INT_CTRL_POL);
if (ret) if (ret)
return ret; return ret;
...@@ -388,19 +444,6 @@ static int ak8974_selftest(struct ak8974 *ak8974) ...@@ -388,19 +444,6 @@ static int ak8974_selftest(struct ak8974 *ak8974)
return 0; 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) static int ak8974_detect(struct ak8974 *ak8974)
{ {
unsigned int whoami; unsigned int whoami;
...@@ -413,9 +456,13 @@ static int ak8974_detect(struct ak8974 *ak8974) ...@@ -413,9 +456,13 @@ static int ak8974_detect(struct ak8974 *ak8974)
if (ret) if (ret)
return ret; return ret;
name = "ami305";
switch (whoami) { switch (whoami) {
case AK8974_WHOAMI_VALUE_AMI306:
name = "ami306";
/* fall-through */
case AK8974_WHOAMI_VALUE_AMI305: case AK8974_WHOAMI_VALUE_AMI305:
name = "ami305";
ret = regmap_read(ak8974->map, AMI305_VER, &fw); ret = regmap_read(ak8974->map, AMI305_VER, &fw);
if (ret) if (ret)
return ret; return ret;
...@@ -602,9 +649,11 @@ static bool ak8974_writeable_reg(struct device *dev, unsigned int reg) ...@@ -602,9 +649,11 @@ static bool ak8974_writeable_reg(struct device *dev, unsigned int reg)
case AMI305_OFFSET_Y + 1: case AMI305_OFFSET_Y + 1:
case AMI305_OFFSET_Z: case AMI305_OFFSET_Z:
case AMI305_OFFSET_Z + 1: case AMI305_OFFSET_Z + 1:
if (ak8974->variant == AK8974_WHOAMI_VALUE_AMI305) return ak8974->variant == AK8974_WHOAMI_VALUE_AMI305 ||
return true; ak8974->variant == AK8974_WHOAMI_VALUE_AMI306;
return false; case AMI306_CTRL4:
case AMI306_CTRL4 + 1:
return ak8974->variant == AK8974_WHOAMI_VALUE_AMI306;
default: default:
return false; return false;
} }
...@@ -678,7 +727,7 @@ static int ak8974_probe(struct i2c_client *i2c, ...@@ -678,7 +727,7 @@ static int ak8974_probe(struct i2c_client *i2c,
ret = ak8974_detect(ak8974); ret = ak8974_detect(ak8974);
if (ret) { 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; goto power_off;
} }
...@@ -827,6 +876,7 @@ static const struct dev_pm_ops ak8974_dev_pm_ops = { ...@@ -827,6 +876,7 @@ static const struct dev_pm_ops ak8974_dev_pm_ops = {
static const struct i2c_device_id ak8974_id[] = { static const struct i2c_device_id ak8974_id[] = {
{"ami305", 0 }, {"ami305", 0 },
{"ami306", 0 },
{"ak8974", 0 }, {"ak8974", 0 },
{} {}
}; };
...@@ -850,7 +900,7 @@ static struct i2c_driver ak8974_driver = { ...@@ -850,7 +900,7 @@ static struct i2c_driver ak8974_driver = {
}; };
module_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("Samu Onkalo");
MODULE_AUTHOR("Linus Walleij"); MODULE_AUTHOR("Linus Walleij");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment