* 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
 #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
 
 #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)
 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;
        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;
        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;
        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;
        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;
        }
 
        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;
        }
 
 
 static const struct i2c_device_id ak8974_id[] = {
        {"ami305", 0 },
+       {"ami306", 0 },
        {"ak8974", 0 },
        {}
 };
 };
 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");