| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Driver for the Yamaha YAS magnetic sensors, often used in Samsung |
| * mobile phones. While all are not yet handled because of lacking |
| * hardware, expand this driver to handle the different variants: |
| * |
| * YAS530 MS-3E (2011 Samsung Galaxy S Advance) |
| * YAS532 MS-3R (2011 Samsung Galaxy S4) |
| * YAS533 MS-3F (Vivo 1633, 1707, V3, Y21L) |
| * (YAS534 is a magnetic switch, not handled) |
| * YAS535 MS-6C |
| * YAS536 MS-3W |
| * YAS537 MS-3T (2015 Samsung Galaxy S6, Note 5, Galaxy S7) |
| * YAS539 MS-3S (2018 Samsung Galaxy A7 SM-A750FN) |
| * |
| * Code functions found in the MPU3050 YAS530 and YAS532 drivers |
| * named "inv_compass" in the Tegra Android kernel tree. |
| * Copyright (C) 2012 InvenSense Corporation |
| * |
| * Code functions for YAS537 based on Yamaha Android kernel driver. |
| * Copyright (c) 2014 Yamaha Corporation |
| * |
| * Author: Linus Walleij <linus.walleij@linaro.org> |
| */ |
| #include <linux/bitfield.h> |
| #include <linux/bitops.h> |
| #include <linux/delay.h> |
| #include <linux/err.h> |
| #include <linux/gpio/consumer.h> |
| #include <linux/i2c.h> |
| #include <linux/module.h> |
| #include <linux/mod_devicetable.h> |
| #include <linux/mutex.h> |
| #include <linux/pm_runtime.h> |
| #include <linux/property.h> |
| #include <linux/regmap.h> |
| #include <linux/regulator/consumer.h> |
| #include <linux/random.h> |
| #include <linux/units.h> |
| |
| #include <linux/iio/buffer.h> |
| #include <linux/iio/iio.h> |
| #include <linux/iio/trigger_consumer.h> |
| #include <linux/iio/triggered_buffer.h> |
| |
| #include <asm/unaligned.h> |
| |
| /* Commonly used registers */ |
| #define YAS5XX_DEVICE_ID 0x80 |
| #define YAS5XX_MEASURE_DATA 0xB0 |
| |
| /* These registers are used by YAS530, YAS532 and YAS533 */ |
| #define YAS530_ACTUATE_INIT_COIL 0x81 |
| #define YAS530_MEASURE 0x82 |
| #define YAS530_CONFIG 0x83 |
| #define YAS530_MEASURE_INTERVAL 0x84 |
| #define YAS530_OFFSET_X 0x85 /* [-31 .. 31] */ |
| #define YAS530_OFFSET_Y1 0x86 /* [-31 .. 31] */ |
| #define YAS530_OFFSET_Y2 0x87 /* [-31 .. 31] */ |
| #define YAS530_TEST1 0x88 |
| #define YAS530_TEST2 0x89 |
| #define YAS530_CAL 0x90 |
| |
| /* Registers used by YAS537 */ |
| #define YAS537_MEASURE 0x81 /* Originally YAS537_REG_CMDR */ |
| #define YAS537_CONFIG 0x82 /* Originally YAS537_REG_CONFR */ |
| #define YAS537_MEASURE_INTERVAL 0x83 /* Originally YAS537_REG_INTRVLR */ |
| #define YAS537_OFFSET_X 0x84 /* Originally YAS537_REG_OXR */ |
| #define YAS537_OFFSET_Y1 0x85 /* Originally YAS537_REG_OY1R */ |
| #define YAS537_OFFSET_Y2 0x86 /* Originally YAS537_REG_OY2R */ |
| #define YAS537_AVR 0x87 |
| #define YAS537_HCK 0x88 |
| #define YAS537_LCK 0x89 |
| #define YAS537_SRST 0x90 |
| #define YAS537_ADCCAL 0x91 |
| #define YAS537_MTC 0x93 |
| #define YAS537_OC 0x9E |
| #define YAS537_TRM 0x9F |
| #define YAS537_CAL 0xC0 |
| |
| /* Bits in the YAS5xx config register */ |
| #define YAS5XX_CONFIG_INTON BIT(0) /* Interrupt on? */ |
| #define YAS5XX_CONFIG_INTHACT BIT(1) /* Interrupt active high? */ |
| #define YAS5XX_CONFIG_CCK_MASK GENMASK(4, 2) |
| #define YAS5XX_CONFIG_CCK_SHIFT 2 |
| |
| /* Bits in the measure command register */ |
| #define YAS5XX_MEASURE_START BIT(0) |
| #define YAS5XX_MEASURE_LDTC BIT(1) |
| #define YAS5XX_MEASURE_FORS BIT(2) |
| #define YAS5XX_MEASURE_DLYMES BIT(4) |
| #define YAS5XX_MEASURE_CONT BIT(5) |
| |
| /* Bits in the measure data register */ |
| #define YAS5XX_MEASURE_DATA_BUSY BIT(7) |
| |
| #define YAS530_DEVICE_ID 0x01 /* YAS530 (MS-3E) */ |
| #define YAS530_VERSION_A 0 /* YAS530 (MS-3E A) */ |
| #define YAS530_VERSION_B 1 /* YAS530B (MS-3E B) */ |
| #define YAS530_VERSION_A_COEF 380 |
| #define YAS530_VERSION_B_COEF 550 |
| #define YAS530_DATA_BITS 12 |
| #define YAS530_DATA_CENTER BIT(YAS530_DATA_BITS - 1) |
| #define YAS530_DATA_OVERFLOW (BIT(YAS530_DATA_BITS) - 1) |
| |
| #define YAS532_DEVICE_ID 0x02 /* YAS532/YAS533 (MS-3R/F) */ |
| #define YAS532_VERSION_AB 0 /* YAS532/533 AB (MS-3R/F AB) */ |
| #define YAS532_VERSION_AC 1 /* YAS532/533 AC (MS-3R/F AC) */ |
| #define YAS532_VERSION_AB_COEF 1800 |
| #define YAS532_VERSION_AC_COEF_X 850 |
| #define YAS532_VERSION_AC_COEF_Y1 750 |
| #define YAS532_VERSION_AC_COEF_Y2 750 |
| #define YAS532_DATA_BITS 13 |
| #define YAS532_DATA_CENTER BIT(YAS532_DATA_BITS - 1) |
| #define YAS532_DATA_OVERFLOW (BIT(YAS532_DATA_BITS) - 1) |
| |
| #define YAS537_DEVICE_ID 0x07 /* YAS537 (MS-3T) */ |
| #define YAS537_VERSION_0 0 /* Version naming unknown */ |
| #define YAS537_VERSION_1 1 /* Version naming unknown */ |
| #define YAS537_MAG_AVERAGE_32_MASK GENMASK(6, 4) |
| #define YAS537_MEASURE_TIME_WORST_US 1500 |
| #define YAS537_DEFAULT_SENSOR_DELAY_MS 50 |
| #define YAS537_MAG_RCOIL_TIME_US 65 |
| #define YAS537_MTC3_MASK_PREP GENMASK(7, 0) |
| #define YAS537_MTC3_MASK_GET GENMASK(7, 5) |
| #define YAS537_MTC3_ADD_BIT BIT(4) |
| #define YAS537_HCK_MASK_PREP GENMASK(4, 0) |
| #define YAS537_HCK_MASK_GET GENMASK(7, 4) |
| #define YAS537_LCK_MASK_PREP GENMASK(4, 0) |
| #define YAS537_LCK_MASK_GET GENMASK(3, 0) |
| #define YAS537_OC_MASK_GET GENMASK(5, 0) |
| |
| /* Turn off device regulators etc after 5 seconds of inactivity */ |
| #define YAS5XX_AUTOSUSPEND_DELAY_MS 5000 |
| |
| enum chip_ids { |
| yas530, |
| yas532, |
| yas533, |
| yas537, |
| }; |
| |
| static const int yas530_volatile_reg[] = { |
| YAS530_ACTUATE_INIT_COIL, |
| YAS530_MEASURE, |
| }; |
| |
| static const int yas537_volatile_reg[] = { |
| YAS537_MEASURE, |
| }; |
| |
| struct yas5xx_calibration { |
| /* Linearization calibration x, y1, y2 */ |
| s32 r[3]; |
| u32 f[3]; |
| /* Temperature compensation calibration */ |
| s16 Cx, Cy1, Cy2; |
| /* Misc calibration coefficients */ |
| s8 a2, a3, a4, a6, a7, a8; |
| s16 a5, a9; |
| u8 k; |
| /* clock divider */ |
| u8 dck; |
| }; |
| |
| struct yas5xx; |
| |
| /** |
| * struct yas5xx_chip_info - device-specific data and function pointers |
| * @devid: device ID number |
| * @product_name: product name of the YAS variant |
| * @version_names: version letters or namings |
| * @volatile_reg: device-specific volatile registers |
| * @volatile_reg_qty: quantity of device-specific volatile registers |
| * @scaling_val2: scaling value for IIO_CHAN_INFO_SCALE |
| * @t_ref: number of counts at reference temperature 20 °C |
| * @min_temp_x10: starting point of temperature counting in 1/10:s degrees Celsius |
| * @get_measure: function pointer to get a measurement |
| * @get_calibration_data: function pointer to get calibration data |
| * @dump_calibration: function pointer to dump calibration for debugging |
| * @measure_offsets: function pointer to measure the offsets |
| * @power_on: function pointer to power-on procedure |
| * |
| * The "t_ref" value for YAS532/533 is known from the Android driver. |
| * For YAS530 and YAS537 it was approximately measured. |
| * |
| * The temperatures "min_temp_x10" are derived from the temperature resolutions |
| * given in the data sheets. |
| */ |
| struct yas5xx_chip_info { |
| unsigned int devid; |
| const char *product_name; |
| const char *version_names[2]; |
| const int *volatile_reg; |
| int volatile_reg_qty; |
| u32 scaling_val2; |
| u16 t_ref; |
| s16 min_temp_x10; |
| int (*get_measure)(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, s32 *zo); |
| int (*get_calibration_data)(struct yas5xx *yas5xx); |
| void (*dump_calibration)(struct yas5xx *yas5xx); |
| int (*measure_offsets)(struct yas5xx *yas5xx); |
| int (*power_on)(struct yas5xx *yas5xx); |
| }; |
| |
| /** |
| * struct yas5xx - state container for the YAS5xx driver |
| * @dev: parent device pointer |
| * @chip_info: device-specific data and function pointers |
| * @version: device version |
| * @calibration: calibration settings from the OTP storage |
| * @hard_offsets: offsets for each axis measured with initcoil actuated |
| * @orientation: mounting matrix, flipped axis etc |
| * @map: regmap to access the YAX5xx registers over I2C |
| * @regs: the vdd and vddio power regulators |
| * @reset: optional GPIO line used for handling RESET |
| * @lock: locks the magnetometer for exclusive use during a measurement (which |
| * involves several register transactions so the regmap lock is not enough) |
| * so that measurements get serialized in a first-come-first serve manner |
| * @scan: naturally aligned measurements |
| */ |
| struct yas5xx { |
| struct device *dev; |
| const struct yas5xx_chip_info *chip_info; |
| unsigned int version; |
| struct yas5xx_calibration calibration; |
| s8 hard_offsets[3]; |
| struct iio_mount_matrix orientation; |
| struct regmap *map; |
| struct regulator_bulk_data regs[2]; |
| struct gpio_desc *reset; |
| struct mutex lock; |
| /* |
| * The scanout is 4 x 32 bits in CPU endianness. |
| * Ensure timestamp is naturally aligned |
| */ |
| struct { |
| s32 channels[4]; |
| s64 ts __aligned(8); |
| } scan; |
| }; |
| |
| /* On YAS530 the x, y1 and y2 values are 12 bits */ |
| static u16 yas530_extract_axis(u8 *data) |
| { |
| u16 val; |
| |
| /* |
| * These are the bits used in a 16bit word: |
| * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 |
| * x x x x x x x x x x x x |
| */ |
| val = get_unaligned_be16(&data[0]); |
| val = FIELD_GET(GENMASK(14, 3), val); |
| return val; |
| } |
| |
| /* On YAS532 the x, y1 and y2 values are 13 bits */ |
| static u16 yas532_extract_axis(u8 *data) |
| { |
| u16 val; |
| |
| /* |
| * These are the bits used in a 16bit word: |
| * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 |
| * x x x x x x x x x x x x x |
| */ |
| val = get_unaligned_be16(&data[0]); |
| val = FIELD_GET(GENMASK(14, 2), val); |
| return val; |
| } |
| |
| /** |
| * yas530_measure() - Make a measure from the hardware |
| * @yas5xx: The device state |
| * @t: the raw temperature measurement |
| * @x: the raw x axis measurement |
| * @y1: the y1 axis measurement |
| * @y2: the y2 axis measurement |
| * @return: 0 on success or error code |
| * |
| * Used by YAS530, YAS532 and YAS533. |
| */ |
| static int yas530_measure(struct yas5xx *yas5xx, u16 *t, u16 *x, u16 *y1, u16 *y2) |
| { |
| const struct yas5xx_chip_info *ci = yas5xx->chip_info; |
| unsigned int busy; |
| u8 data[8]; |
| int ret; |
| u16 val; |
| |
| mutex_lock(&yas5xx->lock); |
| ret = regmap_write(yas5xx->map, YAS530_MEASURE, YAS5XX_MEASURE_START); |
| if (ret < 0) |
| goto out_unlock; |
| |
| /* |
| * Typical time to measure 1500 us, max 2000 us so wait min 500 us |
| * and at most 20000 us (one magnitude more than the datsheet max) |
| * before timeout. |
| */ |
| ret = regmap_read_poll_timeout(yas5xx->map, YAS5XX_MEASURE_DATA, busy, |
| !(busy & YAS5XX_MEASURE_DATA_BUSY), |
| 500, 20000); |
| if (ret) { |
| dev_err(yas5xx->dev, "timeout waiting for measurement\n"); |
| goto out_unlock; |
| } |
| |
| ret = regmap_bulk_read(yas5xx->map, YAS5XX_MEASURE_DATA, |
| data, sizeof(data)); |
| if (ret) |
| goto out_unlock; |
| |
| mutex_unlock(&yas5xx->lock); |
| |
| switch (ci->devid) { |
| case YAS530_DEVICE_ID: |
| /* |
| * The t value is 9 bits in big endian format |
| * These are the bits used in a 16bit word: |
| * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 |
| * x x x x x x x x x |
| */ |
| val = get_unaligned_be16(&data[0]); |
| val = FIELD_GET(GENMASK(14, 6), val); |
| *t = val; |
| *x = yas530_extract_axis(&data[2]); |
| *y1 = yas530_extract_axis(&data[4]); |
| *y2 = yas530_extract_axis(&data[6]); |
| break; |
| case YAS532_DEVICE_ID: |
| /* |
| * The t value is 10 bits in big endian format |
| * These are the bits used in a 16bit word: |
| * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 |
| * x x x x x x x x x x |
| */ |
| val = get_unaligned_be16(&data[0]); |
| val = FIELD_GET(GENMASK(14, 5), val); |
| *t = val; |
| *x = yas532_extract_axis(&data[2]); |
| *y1 = yas532_extract_axis(&data[4]); |
| *y2 = yas532_extract_axis(&data[6]); |
| break; |
| default: |
| dev_err(yas5xx->dev, "unknown data format\n"); |
| ret = -EINVAL; |
| break; |
| } |
| |
| return ret; |
| |
| out_unlock: |
| mutex_unlock(&yas5xx->lock); |
| return ret; |
| } |
| |
| /** |
| * yas537_measure() - Make a measure from the hardware |
| * @yas5xx: The device state |
| * @t: the raw temperature measurement |
| * @x: the raw x axis measurement |
| * @y1: the y1 axis measurement |
| * @y2: the y2 axis measurement |
| * @return: 0 on success or error code |
| */ |
| static int yas537_measure(struct yas5xx *yas5xx, u16 *t, u16 *x, u16 *y1, u16 *y2) |
| { |
| struct yas5xx_calibration *c = &yas5xx->calibration; |
| unsigned int busy; |
| u8 data[8]; |
| u16 xy1y2[3]; |
| s32 h[3], s[3]; |
| int i, ret; |
| |
| mutex_lock(&yas5xx->lock); |
| |
| /* Contrary to YAS530/532, also a "cont" bit is set, meaning unknown */ |
| ret = regmap_write(yas5xx->map, YAS537_MEASURE, YAS5XX_MEASURE_START | |
| YAS5XX_MEASURE_CONT); |
| if (ret < 0) |
| goto out_unlock; |
| |
| /* Use same timeout like YAS530/532 but the bit is in data row 2 */ |
| ret = regmap_read_poll_timeout(yas5xx->map, YAS5XX_MEASURE_DATA + 2, busy, |
| !(busy & YAS5XX_MEASURE_DATA_BUSY), |
| 500, 20000); |
| if (ret) { |
| dev_err(yas5xx->dev, "timeout waiting for measurement\n"); |
| goto out_unlock; |
| } |
| |
| ret = regmap_bulk_read(yas5xx->map, YAS5XX_MEASURE_DATA, |
| data, sizeof(data)); |
| if (ret) |
| goto out_unlock; |
| |
| mutex_unlock(&yas5xx->lock); |
| |
| *t = get_unaligned_be16(&data[0]); |
| xy1y2[0] = FIELD_GET(GENMASK(13, 0), get_unaligned_be16(&data[2])); |
| xy1y2[1] = get_unaligned_be16(&data[4]); |
| xy1y2[2] = get_unaligned_be16(&data[6]); |
| |
| /* The second version of YAS537 needs to include calibration coefficients */ |
| if (yas5xx->version == YAS537_VERSION_1) { |
| for (i = 0; i < 3; i++) |
| s[i] = xy1y2[i] - BIT(13); |
| h[0] = (c->k * (128 * s[0] + c->a2 * s[1] + c->a3 * s[2])) / BIT(13); |
| h[1] = (c->k * (c->a4 * s[0] + c->a5 * s[1] + c->a6 * s[2])) / BIT(13); |
| h[2] = (c->k * (c->a7 * s[0] + c->a8 * s[1] + c->a9 * s[2])) / BIT(13); |
| for (i = 0; i < 3; i++) { |
| clamp_val(h[i], -BIT(13), BIT(13) - 1); |
| xy1y2[i] = h[i] + BIT(13); |
| } |
| } |
| |
| *x = xy1y2[0]; |
| *y1 = xy1y2[1]; |
| *y2 = xy1y2[2]; |
| |
| return 0; |
| |
| out_unlock: |
| mutex_unlock(&yas5xx->lock); |
| return ret; |
| } |
| |
| /* Used by YAS530, YAS532 and YAS533 */ |
| static s32 yas530_linearize(struct yas5xx *yas5xx, u16 val, int axis) |
| { |
| const struct yas5xx_chip_info *ci = yas5xx->chip_info; |
| struct yas5xx_calibration *c = &yas5xx->calibration; |
| static const s32 yas532ac_coef[] = { |
| YAS532_VERSION_AC_COEF_X, |
| YAS532_VERSION_AC_COEF_Y1, |
| YAS532_VERSION_AC_COEF_Y2, |
| }; |
| s32 coef; |
| |
| /* Select coefficients */ |
| switch (ci->devid) { |
| case YAS530_DEVICE_ID: |
| if (yas5xx->version == YAS530_VERSION_A) |
| coef = YAS530_VERSION_A_COEF; |
| else |
| coef = YAS530_VERSION_B_COEF; |
| break; |
| case YAS532_DEVICE_ID: |
| if (yas5xx->version == YAS532_VERSION_AB) |
| coef = YAS532_VERSION_AB_COEF; |
| else |
| /* Elaborate coefficients */ |
| coef = yas532ac_coef[axis]; |
| break; |
| default: |
| dev_err(yas5xx->dev, "unknown device type\n"); |
| return val; |
| } |
| /* |
| * Linearization formula: |
| * |
| * x' = x - (3721 + 50 * f) + (xoffset - r) * c |
| * |
| * Where f and r are calibration values, c is a per-device |
| * and sometimes per-axis coefficient. |
| */ |
| return val - (3721 + 50 * c->f[axis]) + |
| (yas5xx->hard_offsets[axis] - c->r[axis]) * coef; |
| } |
| |
| static s32 yas5xx_calc_temperature(struct yas5xx *yas5xx, u16 t) |
| { |
| const struct yas5xx_chip_info *ci = yas5xx->chip_info; |
| s32 to; |
| u16 t_ref; |
| s16 min_temp_x10; |
| int ref_temp_x10; |
| |
| t_ref = ci->t_ref; |
| min_temp_x10 = ci->min_temp_x10; |
| ref_temp_x10 = 200; |
| |
| to = (min_temp_x10 + ((ref_temp_x10 - min_temp_x10) * t / t_ref)) * 100; |
| return to; |
| } |
| |
| /** |
| * yas530_get_measure() - Measure a sample of all axis and process |
| * @yas5xx: The device state |
| * @to: Temperature out |
| * @xo: X axis out |
| * @yo: Y axis out |
| * @zo: Z axis out |
| * @return: 0 on success or error code |
| * |
| * Used by YAS530, YAS532 and YAS533. |
| */ |
| static int yas530_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, s32 *zo) |
| { |
| const struct yas5xx_chip_info *ci = yas5xx->chip_info; |
| struct yas5xx_calibration *c = &yas5xx->calibration; |
| u16 t_ref, t_comp, t, x, y1, y2; |
| /* These are signed x, signed y1 etc */ |
| s32 sx, sy1, sy2, sy, sz; |
| int ret; |
| |
| /* We first get raw data that needs to be translated to [x,y,z] */ |
| ret = yas530_measure(yas5xx, &t, &x, &y1, &y2); |
| if (ret) |
| return ret; |
| |
| /* Do some linearization if available */ |
| sx = yas530_linearize(yas5xx, x, 0); |
| sy1 = yas530_linearize(yas5xx, y1, 1); |
| sy2 = yas530_linearize(yas5xx, y2, 2); |
| |
| /* |
| * Set the temperature for compensation (unit: counts): |
| * YAS532/YAS533 version AC uses the temperature deviation as a |
| * multiplier. YAS530 and YAS532 version AB use solely the t value. |
| */ |
| t_ref = ci->t_ref; |
| if (ci->devid == YAS532_DEVICE_ID && |
| yas5xx->version == YAS532_VERSION_AC) { |
| t_comp = t - t_ref; |
| } else { |
| t_comp = t; |
| } |
| |
| /* |
| * Temperature compensation for x, y1, y2 respectively: |
| * |
| * Cx * t_comp |
| * x' = x - ----------- |
| * 100 |
| */ |
| sx = sx - (c->Cx * t_comp) / 100; |
| sy1 = sy1 - (c->Cy1 * t_comp) / 100; |
| sy2 = sy2 - (c->Cy2 * t_comp) / 100; |
| |
| /* |
| * Break y1 and y2 into y and z, y1 and y2 are apparently encoding |
| * y and z. |
| */ |
| sy = sy1 - sy2; |
| sz = -sy1 - sy2; |
| |
| /* Calculate temperature readout */ |
| *to = yas5xx_calc_temperature(yas5xx, t); |
| |
| /* |
| * Calibrate [x,y,z] with some formulas like this: |
| * |
| * 100 * x + a_2 * y + a_3 * z |
| * x' = k * --------------------------- |
| * 10 |
| * |
| * a_4 * x + a_5 * y + a_6 * z |
| * y' = k * --------------------------- |
| * 10 |
| * |
| * a_7 * x + a_8 * y + a_9 * z |
| * z' = k * --------------------------- |
| * 10 |
| */ |
| *xo = c->k * ((100 * sx + c->a2 * sy + c->a3 * sz) / 10); |
| *yo = c->k * ((c->a4 * sx + c->a5 * sy + c->a6 * sz) / 10); |
| *zo = c->k * ((c->a7 * sx + c->a8 * sy + c->a9 * sz) / 10); |
| |
| return 0; |
| } |
| |
| /** |
| * yas537_get_measure() - Measure a sample of all axis and process |
| * @yas5xx: The device state |
| * @to: Temperature out |
| * @xo: X axis out |
| * @yo: Y axis out |
| * @zo: Z axis out |
| * @return: 0 on success or error code |
| */ |
| static int yas537_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, s32 *zo) |
| { |
| u16 t, x, y1, y2; |
| int ret; |
| |
| /* We first get raw data that needs to be translated to [x,y,z] */ |
| ret = yas537_measure(yas5xx, &t, &x, &y1, &y2); |
| if (ret) |
| return ret; |
| |
| /* Calculate temperature readout */ |
| *to = yas5xx_calc_temperature(yas5xx, t); |
| |
| /* |
| * Unfortunately, no linearization or temperature compensation formulas |
| * are known for YAS537. |
| */ |
| |
| /* Calculate x, y, z from x, y1, y2 */ |
| *xo = (x - BIT(13)) * 300; |
| *yo = (y1 - y2) * 1732 / 10; |
| *zo = (-y1 - y2 + BIT(14)) * 300; |
| |
| return 0; |
| } |
| |
| static int yas5xx_read_raw(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *chan, |
| int *val, int *val2, |
| long mask) |
| { |
| struct yas5xx *yas5xx = iio_priv(indio_dev); |
| const struct yas5xx_chip_info *ci = yas5xx->chip_info; |
| s32 t, x, y, z; |
| int ret; |
| |
| switch (mask) { |
| case IIO_CHAN_INFO_PROCESSED: |
| case IIO_CHAN_INFO_RAW: |
| pm_runtime_get_sync(yas5xx->dev); |
| ret = ci->get_measure(yas5xx, &t, &x, &y, &z); |
| pm_runtime_mark_last_busy(yas5xx->dev); |
| pm_runtime_put_autosuspend(yas5xx->dev); |
| if (ret) |
| return ret; |
| switch (chan->address) { |
| case 0: |
| *val = t; |
| break; |
| case 1: |
| *val = x; |
| break; |
| case 2: |
| *val = y; |
| break; |
| case 3: |
| *val = z; |
| break; |
| default: |
| dev_err(yas5xx->dev, "unknown channel\n"); |
| return -EINVAL; |
| } |
| return IIO_VAL_INT; |
| case IIO_CHAN_INFO_SCALE: |
| *val = 1; |
| *val2 = ci->scaling_val2; |
| return IIO_VAL_FRACTIONAL; |
| default: |
| /* Unknown request */ |
| return -EINVAL; |
| } |
| } |
| |
| static void yas5xx_fill_buffer(struct iio_dev *indio_dev) |
| { |
| struct yas5xx *yas5xx = iio_priv(indio_dev); |
| const struct yas5xx_chip_info *ci = yas5xx->chip_info; |
| s32 t, x, y, z; |
| int ret; |
| |
| pm_runtime_get_sync(yas5xx->dev); |
| ret = ci->get_measure(yas5xx, &t, &x, &y, &z); |
| pm_runtime_mark_last_busy(yas5xx->dev); |
| pm_runtime_put_autosuspend(yas5xx->dev); |
| if (ret) { |
| dev_err(yas5xx->dev, "error refilling buffer\n"); |
| return; |
| } |
| yas5xx->scan.channels[0] = t; |
| yas5xx->scan.channels[1] = x; |
| yas5xx->scan.channels[2] = y; |
| yas5xx->scan.channels[3] = z; |
| iio_push_to_buffers_with_timestamp(indio_dev, &yas5xx->scan, |
| iio_get_time_ns(indio_dev)); |
| } |
| |
| static irqreturn_t yas5xx_handle_trigger(int irq, void *p) |
| { |
| const struct iio_poll_func *pf = p; |
| struct iio_dev *indio_dev = pf->indio_dev; |
| |
| yas5xx_fill_buffer(indio_dev); |
| iio_trigger_notify_done(indio_dev->trig); |
| |
| return IRQ_HANDLED; |
| } |
| |
| |
| static const struct iio_mount_matrix * |
| yas5xx_get_mount_matrix(const struct iio_dev *indio_dev, |
| const struct iio_chan_spec *chan) |
| { |
| struct yas5xx *yas5xx = iio_priv(indio_dev); |
| |
| return &yas5xx->orientation; |
| } |
| |
| static const struct iio_chan_spec_ext_info yas5xx_ext_info[] = { |
| IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, yas5xx_get_mount_matrix), |
| { } |
| }; |
| |
| #define YAS5XX_AXIS_CHANNEL(axis, index) \ |
| { \ |
| .type = IIO_MAGN, \ |
| .modified = 1, \ |
| .channel2 = IIO_MOD_##axis, \ |
| .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ |
| BIT(IIO_CHAN_INFO_SCALE), \ |
| .ext_info = yas5xx_ext_info, \ |
| .address = index, \ |
| .scan_index = index, \ |
| .scan_type = { \ |
| .sign = 's', \ |
| .realbits = 32, \ |
| .storagebits = 32, \ |
| .endianness = IIO_CPU, \ |
| }, \ |
| } |
| |
| static const struct iio_chan_spec yas5xx_channels[] = { |
| { |
| .type = IIO_TEMP, |
| .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), |
| .address = 0, |
| .scan_index = 0, |
| .scan_type = { |
| .sign = 's', |
| .realbits = 32, |
| .storagebits = 32, |
| .endianness = IIO_CPU, |
| }, |
| }, |
| YAS5XX_AXIS_CHANNEL(X, 1), |
| YAS5XX_AXIS_CHANNEL(Y, 2), |
| YAS5XX_AXIS_CHANNEL(Z, 3), |
| IIO_CHAN_SOFT_TIMESTAMP(4), |
| }; |
| |
| static const unsigned long yas5xx_scan_masks[] = { GENMASK(3, 0), 0 }; |
| |
| static const struct iio_info yas5xx_info = { |
| .read_raw = &yas5xx_read_raw, |
| }; |
| |
| static bool yas5xx_volatile_reg(struct device *dev, unsigned int reg) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct yas5xx *yas5xx = iio_priv(indio_dev); |
| const struct yas5xx_chip_info *ci = yas5xx->chip_info; |
| int reg_qty; |
| int i; |
| |
| if (reg >= YAS5XX_MEASURE_DATA && reg < YAS5XX_MEASURE_DATA + 8) |
| return true; |
| |
| /* |
| * YAS versions share different registers on the same address, |
| * need to differentiate. |
| */ |
| reg_qty = ci->volatile_reg_qty; |
| for (i = 0; i < reg_qty; i++) { |
| if (reg == ci->volatile_reg[i]) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* TODO: enable regmap cache, using mark dirty and sync at runtime resume */ |
| static const struct regmap_config yas5xx_regmap_config = { |
| .reg_bits = 8, |
| .val_bits = 8, |
| .max_register = 0xff, |
| .volatile_reg = yas5xx_volatile_reg, |
| }; |
| |
| /** |
| * yas530_extract_calibration() - extracts the a2-a9 and k calibration |
| * @data: the bitfield to use |
| * @c: the calibration to populate |
| * |
| * Used by YAS530, YAS532 and YAS533. |
| */ |
| static void yas530_extract_calibration(u8 *data, struct yas5xx_calibration *c) |
| { |
| u64 val = get_unaligned_be64(data); |
| |
| /* |
| * Bitfield layout for the axis calibration data, for factor |
| * a2 = 2 etc, k = k, c = clock divider |
| * |
| * n 7 6 5 4 3 2 1 0 |
| * 0 [ 2 2 2 2 2 2 3 3 ] bits 63 .. 56 |
| * 1 [ 3 3 4 4 4 4 4 4 ] bits 55 .. 48 |
| * 2 [ 5 5 5 5 5 5 6 6 ] bits 47 .. 40 |
| * 3 [ 6 6 6 6 7 7 7 7 ] bits 39 .. 32 |
| * 4 [ 7 7 7 8 8 8 8 8 ] bits 31 .. 24 |
| * 5 [ 8 9 9 9 9 9 9 9 ] bits 23 .. 16 |
| * 6 [ 9 k k k k k c c ] bits 15 .. 8 |
| * 7 [ c x x x x x x x ] bits 7 .. 0 |
| */ |
| c->a2 = FIELD_GET(GENMASK_ULL(63, 58), val) - 32; |
| c->a3 = FIELD_GET(GENMASK_ULL(57, 54), val) - 8; |
| c->a4 = FIELD_GET(GENMASK_ULL(53, 48), val) - 32; |
| c->a5 = FIELD_GET(GENMASK_ULL(47, 42), val) + 38; |
| c->a6 = FIELD_GET(GENMASK_ULL(41, 36), val) - 32; |
| c->a7 = FIELD_GET(GENMASK_ULL(35, 29), val) - 64; |
| c->a8 = FIELD_GET(GENMASK_ULL(28, 23), val) - 32; |
| c->a9 = FIELD_GET(GENMASK_ULL(22, 15), val); |
| c->k = FIELD_GET(GENMASK_ULL(14, 10), val) + 10; |
| c->dck = FIELD_GET(GENMASK_ULL(9, 7), val); |
| } |
| |
| static int yas530_get_calibration_data(struct yas5xx *yas5xx) |
| { |
| struct yas5xx_calibration *c = &yas5xx->calibration; |
| u8 data[16]; |
| u32 val; |
| int ret; |
| |
| /* Dummy read, first read is ALWAYS wrong */ |
| ret = regmap_bulk_read(yas5xx->map, YAS530_CAL, data, sizeof(data)); |
| if (ret) |
| return ret; |
| |
| /* Actual calibration readout */ |
| ret = regmap_bulk_read(yas5xx->map, YAS530_CAL, data, sizeof(data)); |
| if (ret) |
| return ret; |
| dev_dbg(yas5xx->dev, "calibration data: %16ph\n", data); |
| |
| /* Contribute calibration data to the input pool for kernel entropy */ |
| add_device_randomness(data, sizeof(data)); |
| |
| /* Extract version */ |
| yas5xx->version = data[15] & GENMASK(1, 0); |
| |
| /* Extract the calibration from the bitfield */ |
| c->Cx = data[0] * 6 - 768; |
| c->Cy1 = data[1] * 6 - 768; |
| c->Cy2 = data[2] * 6 - 768; |
| yas530_extract_calibration(&data[3], c); |
| |
| /* |
| * Extract linearization: |
| * Linearization layout in the 32 bits at byte 11: |
| * The r factors are 6 bit values where bit 5 is the sign |
| * |
| * n 7 6 5 4 3 2 1 0 |
| * 0 [ xx xx xx r0 r0 r0 r0 r0 ] bits 31 .. 24 |
| * 1 [ r0 f0 f0 r1 r1 r1 r1 r1 ] bits 23 .. 16 |
| * 2 [ r1 f1 f1 r2 r2 r2 r2 r2 ] bits 15 .. 8 |
| * 3 [ r2 f2 f2 xx xx xx xx xx ] bits 7 .. 0 |
| */ |
| val = get_unaligned_be32(&data[11]); |
| c->f[0] = FIELD_GET(GENMASK(22, 21), val); |
| c->f[1] = FIELD_GET(GENMASK(14, 13), val); |
| c->f[2] = FIELD_GET(GENMASK(6, 5), val); |
| c->r[0] = sign_extend32(FIELD_GET(GENMASK(28, 23), val), 5); |
| c->r[1] = sign_extend32(FIELD_GET(GENMASK(20, 15), val), 5); |
| c->r[2] = sign_extend32(FIELD_GET(GENMASK(12, 7), val), 5); |
| |
| return 0; |
| } |
| |
| static int yas532_get_calibration_data(struct yas5xx *yas5xx) |
| { |
| struct yas5xx_calibration *c = &yas5xx->calibration; |
| u8 data[14]; |
| u32 val; |
| int ret; |
| |
| /* Dummy read, first read is ALWAYS wrong */ |
| ret = regmap_bulk_read(yas5xx->map, YAS530_CAL, data, sizeof(data)); |
| if (ret) |
| return ret; |
| /* Actual calibration readout */ |
| ret = regmap_bulk_read(yas5xx->map, YAS530_CAL, data, sizeof(data)); |
| if (ret) |
| return ret; |
| dev_dbg(yas5xx->dev, "calibration data: %14ph\n", data); |
| |
| /* Sanity check, is this all zeroes? */ |
| if (!memchr_inv(data, 0x00, 13) && !(data[13] & BIT(7))) |
| dev_warn(yas5xx->dev, "calibration is blank!\n"); |
| |
| /* Contribute calibration data to the input pool for kernel entropy */ |
| add_device_randomness(data, sizeof(data)); |
| |
| /* Only one bit of version info reserved here as far as we know */ |
| yas5xx->version = data[13] & BIT(0); |
| |
| /* Extract calibration from the bitfield */ |
| c->Cx = data[0] * 10 - 1280; |
| c->Cy1 = data[1] * 10 - 1280; |
| c->Cy2 = data[2] * 10 - 1280; |
| yas530_extract_calibration(&data[3], c); |
| |
| /* |
| * Extract linearization: |
| * Linearization layout in the 32 bits at byte 10: |
| * The r factors are 6 bit values where bit 5 is the sign |
| * |
| * n 7 6 5 4 3 2 1 0 |
| * 0 [ xx r0 r0 r0 r0 r0 r0 f0 ] bits 31 .. 24 |
| * 1 [ f0 r1 r1 r1 r1 r1 r1 f1 ] bits 23 .. 16 |
| * 2 [ f1 r2 r2 r2 r2 r2 r2 f2 ] bits 15 .. 8 |
| * 3 [ f2 xx xx xx xx xx xx xx ] bits 7 .. 0 |
| */ |
| val = get_unaligned_be32(&data[10]); |
| c->f[0] = FIELD_GET(GENMASK(24, 23), val); |
| c->f[1] = FIELD_GET(GENMASK(16, 15), val); |
| c->f[2] = FIELD_GET(GENMASK(8, 7), val); |
| c->r[0] = sign_extend32(FIELD_GET(GENMASK(30, 25), val), 5); |
| c->r[1] = sign_extend32(FIELD_GET(GENMASK(22, 17), val), 5); |
| c->r[2] = sign_extend32(FIELD_GET(GENMASK(14, 7), val), 5); |
| |
| return 0; |
| } |
| |
| static int yas537_get_calibration_data(struct yas5xx *yas5xx) |
| { |
| struct yas5xx_calibration *c = &yas5xx->calibration; |
| u8 data[17]; |
| u32 val1, val2, val3, val4; |
| int i, ret; |
| |
| /* Writing SRST register */ |
| ret = regmap_write(yas5xx->map, YAS537_SRST, BIT(1)); |
| if (ret) |
| return ret; |
| |
| /* Calibration readout, YAS537 needs one readout only */ |
| ret = regmap_bulk_read(yas5xx->map, YAS537_CAL, data, sizeof(data)); |
| if (ret) |
| return ret; |
| dev_dbg(yas5xx->dev, "calibration data: %17ph\n", data); |
| |
| /* Sanity check, is this all zeroes? */ |
| if (!memchr_inv(data, 0x00, 16) && !FIELD_GET(GENMASK(5, 0), data[16])) |
| dev_warn(yas5xx->dev, "calibration is blank!\n"); |
| |
| /* Contribute calibration data to the input pool for kernel entropy */ |
| add_device_randomness(data, sizeof(data)); |
| |
| /* Extract version information */ |
| yas5xx->version = FIELD_GET(GENMASK(7, 6), data[16]); |
| |
| /* There are two versions of YAS537 behaving differently */ |
| switch (yas5xx->version) { |
| case YAS537_VERSION_0: |
| /* |
| * The first version simply writes data back into registers: |
| * |
| * data[0] YAS537_MTC 0x93 |
| * data[1] 0x94 |
| * data[2] 0x95 |
| * data[3] 0x96 |
| * data[4] 0x97 |
| * data[5] 0x98 |
| * data[6] 0x99 |
| * data[7] 0x9a |
| * data[8] 0x9b |
| * data[9] 0x9c |
| * data[10] 0x9d |
| * data[11] YAS537_OC 0x9e |
| * |
| * data[12] YAS537_OFFSET_X 0x84 |
| * data[13] YAS537_OFFSET_Y1 0x85 |
| * data[14] YAS537_OFFSET_Y2 0x86 |
| * |
| * data[15] YAS537_HCK 0x88 |
| * data[16] YAS537_LCK 0x89 |
| */ |
| for (i = 0; i < 12; i++) { |
| ret = regmap_write(yas5xx->map, YAS537_MTC + i, |
| data[i]); |
| if (ret) |
| return ret; |
| } |
| for (i = 0; i < 3; i++) { |
| ret = regmap_write(yas5xx->map, YAS537_OFFSET_X + i, |
| data[i + 12]); |
| if (ret) |
| return ret; |
| yas5xx->hard_offsets[i] = data[i + 12]; |
| } |
| for (i = 0; i < 2; i++) { |
| ret = regmap_write(yas5xx->map, YAS537_HCK + i, |
| data[i + 15]); |
| if (ret) |
| return ret; |
| } |
| break; |
| case YAS537_VERSION_1: |
| /* |
| * The second version writes some data into registers but also |
| * extracts calibration coefficients. |
| * |
| * Registers being written: |
| * |
| * data[0] YAS537_MTC 0x93 |
| * data[1] YAS537_MTC+1 0x94 |
| * data[2] YAS537_MTC+2 0x95 |
| * data[3] YAS537_MTC+3 (partially) 0x96 |
| * |
| * data[12] YAS537_OFFSET_X 0x84 |
| * data[13] YAS537_OFFSET_Y1 0x85 |
| * data[14] YAS537_OFFSET_Y2 0x86 |
| * |
| * data[15] YAS537_HCK (partially) 0x88 |
| * YAS537_LCK (partially) 0x89 |
| * data[16] YAS537_OC (partially) 0x9e |
| */ |
| for (i = 0; i < 3; i++) { |
| ret = regmap_write(yas5xx->map, YAS537_MTC + i, |
| data[i]); |
| if (ret) |
| return ret; |
| } |
| for (i = 0; i < 3; i++) { |
| ret = regmap_write(yas5xx->map, YAS537_OFFSET_X + i, |
| data[i + 12]); |
| if (ret) |
| return ret; |
| yas5xx->hard_offsets[i] = data[i + 12]; |
| } |
| /* |
| * Visualization of partially taken data: |
| * |
| * data[3] n 7 6 5 4 3 2 1 0 |
| * YAS537_MTC+3 x x x 1 0 0 0 0 |
| * |
| * data[15] n 7 6 5 4 3 2 1 0 |
| * YAS537_HCK x x x x 0 |
| * |
| * data[15] n 7 6 5 4 3 2 1 0 |
| * YAS537_LCK x x x x 0 |
| * |
| * data[16] n 7 6 5 4 3 2 1 0 |
| * YAS537_OC x x x x x x |
| */ |
| ret = regmap_write(yas5xx->map, YAS537_MTC + 3, |
| FIELD_PREP(YAS537_MTC3_MASK_PREP, |
| FIELD_GET(YAS537_MTC3_MASK_GET, data[3])) | |
| YAS537_MTC3_ADD_BIT); |
| if (ret) |
| return ret; |
| ret = regmap_write(yas5xx->map, YAS537_HCK, |
| FIELD_PREP(YAS537_HCK_MASK_PREP, |
| FIELD_GET(YAS537_HCK_MASK_GET, data[15]))); |
| if (ret) |
| return ret; |
| ret = regmap_write(yas5xx->map, YAS537_LCK, |
| FIELD_PREP(YAS537_LCK_MASK_PREP, |
| FIELD_GET(YAS537_LCK_MASK_GET, data[15]))); |
| if (ret) |
| return ret; |
| ret = regmap_write(yas5xx->map, YAS537_OC, |
| FIELD_GET(YAS537_OC_MASK_GET, data[16])); |
| if (ret) |
| return ret; |
| /* |
| * For data extraction, build some blocks. Four 32-bit blocks |
| * look appropriate. |
| * |
| * n 7 6 5 4 3 2 1 0 |
| * data[0] 0 [ Cx Cx Cx Cx Cx Cx Cx Cx ] bits 31 .. 24 |
| * data[1] 1 [ Cx C1 C1 C1 C1 C1 C1 C1 ] bits 23 .. 16 |
| * data[2] 2 [ C1 C1 C2 C2 C2 C2 C2 C2 ] bits 15 .. 8 |
| * data[3] 3 [ C2 C2 C2 ] bits 7 .. 0 |
| * |
| * n 7 6 5 4 3 2 1 0 |
| * data[3] 0 [ a2 a2 a2 a2 a2 ] bits 31 .. 24 |
| * data[4] 1 [ a2 a2 a3 a3 a3 a3 a3 a3 ] bits 23 .. 16 |
| * data[5] 2 [ a3 a4 a4 a4 a4 a4 a4 a4 ] bits 15 .. 8 |
| * data[6] 3 [ a4 ] bits 7 .. 0 |
| * |
| * n 7 6 5 4 3 2 1 0 |
| * data[6] 0 [ a5 a5 a5 a5 a5 a5 a5 ] bits 31 .. 24 |
| * data[7] 1 [ a5 a5 a6 a6 a6 a6 a6 a6 ] bits 23 .. 16 |
| * data[8] 2 [ a6 a7 a7 a7 a7 a7 a7 a7 ] bits 15 .. 8 |
| * data[9] 3 [ a7 ] bits 7 .. 0 |
| * |
| * n 7 6 5 4 3 2 1 0 |
| * data[9] 0 [ a8 a8 a8 a8 a8 a8 a8 ] bits 31 .. 24 |
| * data[10] 1 [ a9 a9 a9 a9 a9 a9 a9 a9 ] bits 23 .. 16 |
| * data[11] 2 [ a9 k k k k k k k ] bits 15 .. 8 |
| * data[12] 3 [ ] bits 7 .. 0 |
| */ |
| val1 = get_unaligned_be32(&data[0]); |
| val2 = get_unaligned_be32(&data[3]); |
| val3 = get_unaligned_be32(&data[6]); |
| val4 = get_unaligned_be32(&data[9]); |
| /* Extract calibration coefficients and modify */ |
| c->Cx = FIELD_GET(GENMASK(31, 23), val1) - 256; |
| c->Cy1 = FIELD_GET(GENMASK(22, 14), val1) - 256; |
| c->Cy2 = FIELD_GET(GENMASK(13, 5), val1) - 256; |
| c->a2 = FIELD_GET(GENMASK(28, 22), val2) - 64; |
| c->a3 = FIELD_GET(GENMASK(21, 15), val2) - 64; |
| c->a4 = FIELD_GET(GENMASK(14, 7), val2) - 128; |
| c->a5 = FIELD_GET(GENMASK(30, 22), val3) - 112; |
| c->a6 = FIELD_GET(GENMASK(21, 15), val3) - 64; |
| c->a7 = FIELD_GET(GENMASK(14, 7), val3) - 128; |
| c->a8 = FIELD_GET(GENMASK(30, 24), val4) - 64; |
| c->a9 = FIELD_GET(GENMASK(23, 15), val4) - 112; |
| c->k = FIELD_GET(GENMASK(14, 8), val4); |
| break; |
| default: |
| dev_err(yas5xx->dev, "unknown version of YAS537\n"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /* Used by YAS530, YAS532 and YAS533 */ |
| static void yas530_dump_calibration(struct yas5xx *yas5xx) |
| { |
| struct yas5xx_calibration *c = &yas5xx->calibration; |
| |
| dev_dbg(yas5xx->dev, "f[] = [%d, %d, %d]\n", |
| c->f[0], c->f[1], c->f[2]); |
| dev_dbg(yas5xx->dev, "r[] = [%d, %d, %d]\n", |
| c->r[0], c->r[1], c->r[2]); |
| dev_dbg(yas5xx->dev, "Cx = %d\n", c->Cx); |
| dev_dbg(yas5xx->dev, "Cy1 = %d\n", c->Cy1); |
| dev_dbg(yas5xx->dev, "Cy2 = %d\n", c->Cy2); |
| dev_dbg(yas5xx->dev, "a2 = %d\n", c->a2); |
| dev_dbg(yas5xx->dev, "a3 = %d\n", c->a3); |
| dev_dbg(yas5xx->dev, "a4 = %d\n", c->a4); |
| dev_dbg(yas5xx->dev, "a5 = %d\n", c->a5); |
| dev_dbg(yas5xx->dev, "a6 = %d\n", c->a6); |
| dev_dbg(yas5xx->dev, "a7 = %d\n", c->a7); |
| dev_dbg(yas5xx->dev, "a8 = %d\n", c->a8); |
| dev_dbg(yas5xx->dev, "a9 = %d\n", c->a9); |
| dev_dbg(yas5xx->dev, "k = %d\n", c->k); |
| dev_dbg(yas5xx->dev, "dck = %d\n", c->dck); |
| } |
| |
| static void yas537_dump_calibration(struct yas5xx *yas5xx) |
| { |
| struct yas5xx_calibration *c = &yas5xx->calibration; |
| |
| if (yas5xx->version == YAS537_VERSION_1) { |
| dev_dbg(yas5xx->dev, "Cx = %d\n", c->Cx); |
| dev_dbg(yas5xx->dev, "Cy1 = %d\n", c->Cy1); |
| dev_dbg(yas5xx->dev, "Cy2 = %d\n", c->Cy2); |
| dev_dbg(yas5xx->dev, "a2 = %d\n", c->a2); |
| dev_dbg(yas5xx->dev, "a3 = %d\n", c->a3); |
| dev_dbg(yas5xx->dev, "a4 = %d\n", c->a4); |
| dev_dbg(yas5xx->dev, "a5 = %d\n", c->a5); |
| dev_dbg(yas5xx->dev, "a6 = %d\n", c->a6); |
| dev_dbg(yas5xx->dev, "a7 = %d\n", c->a7); |
| dev_dbg(yas5xx->dev, "a8 = %d\n", c->a8); |
| dev_dbg(yas5xx->dev, "a9 = %d\n", c->a9); |
| dev_dbg(yas5xx->dev, "k = %d\n", c->k); |
| } |
| } |
| |
| /* Used by YAS530, YAS532 and YAS533 */ |
| static int yas530_set_offsets(struct yas5xx *yas5xx, s8 ox, s8 oy1, s8 oy2) |
| { |
| int ret; |
| |
| ret = regmap_write(yas5xx->map, YAS530_OFFSET_X, ox); |
| if (ret) |
| return ret; |
| ret = regmap_write(yas5xx->map, YAS530_OFFSET_Y1, oy1); |
| if (ret) |
| return ret; |
| return regmap_write(yas5xx->map, YAS530_OFFSET_Y2, oy2); |
| } |
| |
| /* Used by YAS530, YAS532 and YAS533 */ |
| static s8 yas530_adjust_offset(s8 old, int bit, u16 center, u16 measure) |
| { |
| if (measure > center) |
| return old + BIT(bit); |
| if (measure < center) |
| return old - BIT(bit); |
| return old; |
| } |
| |
| /* Used by YAS530, YAS532 and YAS533 */ |
| static int yas530_measure_offsets(struct yas5xx *yas5xx) |
| { |
| const struct yas5xx_chip_info *ci = yas5xx->chip_info; |
| int ret; |
| u16 center; |
| u16 t, x, y1, y2; |
| s8 ox, oy1, oy2; |
| int i; |
| |
| /* Actuate the init coil and measure offsets */ |
| ret = regmap_write(yas5xx->map, YAS530_ACTUATE_INIT_COIL, 0); |
| if (ret) |
| return ret; |
| |
| /* When the initcoil is active this should be around the center */ |
| switch (ci->devid) { |
| case YAS530_DEVICE_ID: |
| center = YAS530_DATA_CENTER; |
| break; |
| case YAS532_DEVICE_ID: |
| center = YAS532_DATA_CENTER; |
| break; |
| default: |
| dev_err(yas5xx->dev, "unknown device type\n"); |
| return -EINVAL; |
| } |
| |
| /* |
| * We set offsets in the interval +-31 by iterating |
| * +-16, +-8, +-4, +-2, +-1 adjusting the offsets each |
| * time, then writing the final offsets into the |
| * registers. |
| * |
| * NOTE: these offsets are NOT in the same unit or magnitude |
| * as the values for [x, y1, y2]. The value is +/-31 |
| * but the effect on the raw values is much larger. |
| * The effect of the offset is to bring the measure |
| * rougly to the center. |
| */ |
| ox = 0; |
| oy1 = 0; |
| oy2 = 0; |
| |
| for (i = 4; i >= 0; i--) { |
| ret = yas530_set_offsets(yas5xx, ox, oy1, oy2); |
| if (ret) |
| return ret; |
| |
| ret = yas530_measure(yas5xx, &t, &x, &y1, &y2); |
| if (ret) |
| return ret; |
| dev_dbg(yas5xx->dev, "measurement %d: x=%d, y1=%d, y2=%d\n", |
| 5-i, x, y1, y2); |
| |
| ox = yas530_adjust_offset(ox, i, center, x); |
| oy1 = yas530_adjust_offset(oy1, i, center, y1); |
| oy2 = yas530_adjust_offset(oy2, i, center, y2); |
| } |
| |
| /* Needed for calibration algorithm */ |
| yas5xx->hard_offsets[0] = ox; |
| yas5xx->hard_offsets[1] = oy1; |
| yas5xx->hard_offsets[2] = oy2; |
| ret = yas530_set_offsets(yas5xx, ox, oy1, oy2); |
| if (ret) |
| return ret; |
| |
| dev_info(yas5xx->dev, "discovered hard offsets: x=%d, y1=%d, y2=%d\n", |
| ox, oy1, oy2); |
| return 0; |
| } |
| |
| /* Used by YAS530, YAS532 and YAS533 */ |
| static int yas530_power_on(struct yas5xx *yas5xx) |
| { |
| unsigned int val; |
| int ret; |
| |
| /* Zero the test registers */ |
| ret = regmap_write(yas5xx->map, YAS530_TEST1, 0); |
| if (ret) |
| return ret; |
| ret = regmap_write(yas5xx->map, YAS530_TEST2, 0); |
| if (ret) |
| return ret; |
| |
| /* Set up for no interrupts, calibrated clock divider */ |
| val = FIELD_PREP(YAS5XX_CONFIG_CCK_MASK, yas5xx->calibration.dck); |
| ret = regmap_write(yas5xx->map, YAS530_CONFIG, val); |
| if (ret) |
| return ret; |
| |
| /* Measure interval 0 (back-to-back?) */ |
| return regmap_write(yas5xx->map, YAS530_MEASURE_INTERVAL, 0); |
| } |
| |
| static int yas537_power_on(struct yas5xx *yas5xx) |
| { |
| __be16 buf; |
| int ret; |
| u8 intrvl; |
| |
| /* Writing ADCCAL and TRM registers */ |
| buf = cpu_to_be16(GENMASK(9, 3)); |
| ret = regmap_bulk_write(yas5xx->map, YAS537_ADCCAL, &buf, sizeof(buf)); |
| if (ret) |
| return ret; |
| ret = regmap_write(yas5xx->map, YAS537_TRM, GENMASK(7, 0)); |
| if (ret) |
| return ret; |
| |
| /* The interval value is static in regular operation */ |
| intrvl = (YAS537_DEFAULT_SENSOR_DELAY_MS * MILLI |
| - YAS537_MEASURE_TIME_WORST_US) / 4100; |
| ret = regmap_write(yas5xx->map, YAS537_MEASURE_INTERVAL, intrvl); |
| if (ret) |
| return ret; |
| |
| /* The average value is also static in regular operation */ |
| ret = regmap_write(yas5xx->map, YAS537_AVR, YAS537_MAG_AVERAGE_32_MASK); |
| if (ret) |
| return ret; |
| |
| /* Perform the "rcoil" part but skip the "last_after_rcoil" read */ |
| ret = regmap_write(yas5xx->map, YAS537_CONFIG, BIT(3)); |
| if (ret) |
| return ret; |
| |
| /* Wait until the coil has ramped up */ |
| usleep_range(YAS537_MAG_RCOIL_TIME_US, YAS537_MAG_RCOIL_TIME_US + 100); |
| |
| return 0; |
| } |
| |
| static const struct yas5xx_chip_info yas5xx_chip_info_tbl[] = { |
| [yas530] = { |
| .devid = YAS530_DEVICE_ID, |
| .product_name = "YAS530 MS-3E", |
| .version_names = { "A", "B" }, |
| .volatile_reg = yas530_volatile_reg, |
| .volatile_reg_qty = ARRAY_SIZE(yas530_volatile_reg), |
| .scaling_val2 = 100000000, /* picotesla to Gauss */ |
| .t_ref = 182, /* counts */ |
| .min_temp_x10 = -620, /* 1/10:s degrees Celsius */ |
| .get_measure = yas530_get_measure, |
| .get_calibration_data = yas530_get_calibration_data, |
| .dump_calibration = yas530_dump_calibration, |
| .measure_offsets = yas530_measure_offsets, |
| .power_on = yas530_power_on, |
| }, |
| [yas532] = { |
| .devid = YAS532_DEVICE_ID, |
| .product_name = "YAS532 MS-3R", |
| .version_names = { "AB", "AC" }, |
| .volatile_reg = yas530_volatile_reg, |
| .volatile_reg_qty = ARRAY_SIZE(yas530_volatile_reg), |
| .scaling_val2 = 100000, /* nanotesla to Gauss */ |
| .t_ref = 390, /* counts */ |
| .min_temp_x10 = -500, /* 1/10:s degrees Celsius */ |
| .get_measure = yas530_get_measure, |
| .get_calibration_data = yas532_get_calibration_data, |
| .dump_calibration = yas530_dump_calibration, |
| .measure_offsets = yas530_measure_offsets, |
| .power_on = yas530_power_on, |
| }, |
| [yas533] = { |
| .devid = YAS532_DEVICE_ID, |
| .product_name = "YAS533 MS-3F", |
| .version_names = { "AB", "AC" }, |
| .volatile_reg = yas530_volatile_reg, |
| .volatile_reg_qty = ARRAY_SIZE(yas530_volatile_reg), |
| .scaling_val2 = 100000, /* nanotesla to Gauss */ |
| .t_ref = 390, /* counts */ |
| .min_temp_x10 = -500, /* 1/10:s degrees Celsius */ |
| .get_measure = yas530_get_measure, |
| .get_calibration_data = yas532_get_calibration_data, |
| .dump_calibration = yas530_dump_calibration, |
| .measure_offsets = yas530_measure_offsets, |
| .power_on = yas530_power_on, |
| }, |
| [yas537] = { |
| .devid = YAS537_DEVICE_ID, |
| .product_name = "YAS537 MS-3T", |
| .version_names = { "v0", "v1" }, /* version naming unknown */ |
| .volatile_reg = yas537_volatile_reg, |
| .volatile_reg_qty = ARRAY_SIZE(yas537_volatile_reg), |
| .scaling_val2 = 100000, /* nanotesla to Gauss */ |
| .t_ref = 8120, /* counts */ |
| .min_temp_x10 = -3860, /* 1/10:s degrees Celsius */ |
| .get_measure = yas537_get_measure, |
| .get_calibration_data = yas537_get_calibration_data, |
| .dump_calibration = yas537_dump_calibration, |
| /* .measure_offets is not needed for yas537 */ |
| .power_on = yas537_power_on, |
| }, |
| }; |
| |
| static int yas5xx_probe(struct i2c_client *i2c) |
| { |
| const struct i2c_device_id *id = i2c_client_get_device_id(i2c); |
| struct iio_dev *indio_dev; |
| struct device *dev = &i2c->dev; |
| struct yas5xx *yas5xx; |
| const struct yas5xx_chip_info *ci; |
| int id_check; |
| int ret; |
| |
| indio_dev = devm_iio_device_alloc(dev, sizeof(*yas5xx)); |
| if (!indio_dev) |
| return -ENOMEM; |
| |
| yas5xx = iio_priv(indio_dev); |
| i2c_set_clientdata(i2c, indio_dev); |
| yas5xx->dev = dev; |
| mutex_init(&yas5xx->lock); |
| |
| ret = iio_read_mount_matrix(dev, &yas5xx->orientation); |
| if (ret) |
| return ret; |
| |
| yas5xx->regs[0].supply = "vdd"; |
| yas5xx->regs[1].supply = "iovdd"; |
| ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(yas5xx->regs), |
| yas5xx->regs); |
| if (ret) |
| return dev_err_probe(dev, ret, "cannot get regulators\n"); |
| |
| ret = regulator_bulk_enable(ARRAY_SIZE(yas5xx->regs), yas5xx->regs); |
| if (ret) |
| return dev_err_probe(dev, ret, "cannot enable regulators\n"); |
| |
| /* See comment in runtime resume callback */ |
| usleep_range(31000, 40000); |
| |
| /* This will take the device out of reset if need be */ |
| yas5xx->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); |
| if (IS_ERR(yas5xx->reset)) { |
| ret = dev_err_probe(dev, PTR_ERR(yas5xx->reset), "failed to get reset line\n"); |
| goto reg_off; |
| } |
| |
| yas5xx->map = devm_regmap_init_i2c(i2c, &yas5xx_regmap_config); |
| if (IS_ERR(yas5xx->map)) { |
| ret = dev_err_probe(dev, PTR_ERR(yas5xx->map), "failed to allocate register map\n"); |
| goto assert_reset; |
| } |
| |
| ci = device_get_match_data(dev); |
| if (!ci) |
| ci = (const struct yas5xx_chip_info *)id->driver_data; |
| yas5xx->chip_info = ci; |
| |
| ret = regmap_read(yas5xx->map, YAS5XX_DEVICE_ID, &id_check); |
| if (ret) |
| goto assert_reset; |
| |
| if (id_check != ci->devid) { |
| ret = dev_err_probe(dev, -ENODEV, |
| "device ID %02x doesn't match %s\n", |
| id_check, id->name); |
| goto assert_reset; |
| } |
| |
| ret = ci->get_calibration_data(yas5xx); |
| if (ret) |
| goto assert_reset; |
| |
| dev_info(dev, "detected %s %s\n", ci->product_name, |
| ci->version_names[yas5xx->version]); |
| |
| ci->dump_calibration(yas5xx); |
| |
| ret = ci->power_on(yas5xx); |
| if (ret) |
| goto assert_reset; |
| |
| if (ci->measure_offsets) { |
| ret = ci->measure_offsets(yas5xx); |
| if (ret) |
| goto assert_reset; |
| } |
| |
| indio_dev->info = &yas5xx_info; |
| indio_dev->available_scan_masks = yas5xx_scan_masks; |
| indio_dev->modes = INDIO_DIRECT_MODE; |
| indio_dev->name = id->name; |
| indio_dev->channels = yas5xx_channels; |
| indio_dev->num_channels = ARRAY_SIZE(yas5xx_channels); |
| |
| ret = iio_triggered_buffer_setup(indio_dev, NULL, |
| yas5xx_handle_trigger, |
| NULL); |
| if (ret) { |
| dev_err_probe(dev, ret, "triggered buffer setup failed\n"); |
| goto assert_reset; |
| } |
| |
| ret = iio_device_register(indio_dev); |
| if (ret) { |
| dev_err_probe(dev, ret, "device register failed\n"); |
| goto cleanup_buffer; |
| } |
| |
| /* Take runtime PM online */ |
| pm_runtime_get_noresume(dev); |
| pm_runtime_set_active(dev); |
| pm_runtime_enable(dev); |
| |
| pm_runtime_set_autosuspend_delay(dev, YAS5XX_AUTOSUSPEND_DELAY_MS); |
| pm_runtime_use_autosuspend(dev); |
| pm_runtime_put(dev); |
| |
| return 0; |
| |
| cleanup_buffer: |
| iio_triggered_buffer_cleanup(indio_dev); |
| assert_reset: |
| gpiod_set_value_cansleep(yas5xx->reset, 1); |
| reg_off: |
| regulator_bulk_disable(ARRAY_SIZE(yas5xx->regs), yas5xx->regs); |
| |
| return ret; |
| } |
| |
| static void yas5xx_remove(struct i2c_client *i2c) |
| { |
| struct iio_dev *indio_dev = i2c_get_clientdata(i2c); |
| struct yas5xx *yas5xx = iio_priv(indio_dev); |
| struct device *dev = &i2c->dev; |
| |
| iio_device_unregister(indio_dev); |
| iio_triggered_buffer_cleanup(indio_dev); |
| /* |
| * Now we can't get any more reads from the device, which would |
| * also call pm_runtime* functions and race with our disable |
| * code. Disable PM runtime in orderly fashion and power down. |
| */ |
| pm_runtime_get_sync(dev); |
| pm_runtime_put_noidle(dev); |
| pm_runtime_disable(dev); |
| gpiod_set_value_cansleep(yas5xx->reset, 1); |
| regulator_bulk_disable(ARRAY_SIZE(yas5xx->regs), yas5xx->regs); |
| } |
| |
| static int yas5xx_runtime_suspend(struct device *dev) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct yas5xx *yas5xx = iio_priv(indio_dev); |
| |
| gpiod_set_value_cansleep(yas5xx->reset, 1); |
| regulator_bulk_disable(ARRAY_SIZE(yas5xx->regs), yas5xx->regs); |
| |
| return 0; |
| } |
| |
| static int yas5xx_runtime_resume(struct device *dev) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct yas5xx *yas5xx = iio_priv(indio_dev); |
| const struct yas5xx_chip_info *ci = yas5xx->chip_info; |
| int ret; |
| |
| ret = regulator_bulk_enable(ARRAY_SIZE(yas5xx->regs), yas5xx->regs); |
| if (ret) { |
| dev_err(dev, "cannot enable regulators\n"); |
| return ret; |
| } |
| |
| /* |
| * The YAS530 datasheet says TVSKW is up to 30 ms, after that 1 ms |
| * for all voltages to settle. The YAS532 is 10ms then 4ms for the |
| * I2C to come online. Let's keep it safe and put this at 31ms. |
| */ |
| usleep_range(31000, 40000); |
| gpiod_set_value_cansleep(yas5xx->reset, 0); |
| |
| ret = ci->power_on(yas5xx); |
| if (ret) { |
| dev_err(dev, "cannot power on\n"); |
| goto out_reset; |
| } |
| |
| return 0; |
| |
| out_reset: |
| gpiod_set_value_cansleep(yas5xx->reset, 1); |
| regulator_bulk_disable(ARRAY_SIZE(yas5xx->regs), yas5xx->regs); |
| |
| return ret; |
| } |
| |
| static DEFINE_RUNTIME_DEV_PM_OPS(yas5xx_dev_pm_ops, yas5xx_runtime_suspend, |
| yas5xx_runtime_resume, NULL); |
| |
| static const struct i2c_device_id yas5xx_id[] = { |
| {"yas530", (kernel_ulong_t)&yas5xx_chip_info_tbl[yas530] }, |
| {"yas532", (kernel_ulong_t)&yas5xx_chip_info_tbl[yas532] }, |
| {"yas533", (kernel_ulong_t)&yas5xx_chip_info_tbl[yas533] }, |
| {"yas537", (kernel_ulong_t)&yas5xx_chip_info_tbl[yas537] }, |
| {} |
| }; |
| MODULE_DEVICE_TABLE(i2c, yas5xx_id); |
| |
| static const struct of_device_id yas5xx_of_match[] = { |
| { .compatible = "yamaha,yas530", &yas5xx_chip_info_tbl[yas530] }, |
| { .compatible = "yamaha,yas532", &yas5xx_chip_info_tbl[yas532] }, |
| { .compatible = "yamaha,yas533", &yas5xx_chip_info_tbl[yas533] }, |
| { .compatible = "yamaha,yas537", &yas5xx_chip_info_tbl[yas537] }, |
| {} |
| }; |
| MODULE_DEVICE_TABLE(of, yas5xx_of_match); |
| |
| static struct i2c_driver yas5xx_driver = { |
| .driver = { |
| .name = "yas5xx", |
| .of_match_table = yas5xx_of_match, |
| .pm = pm_ptr(&yas5xx_dev_pm_ops), |
| }, |
| .probe = yas5xx_probe, |
| .remove = yas5xx_remove, |
| .id_table = yas5xx_id, |
| }; |
| module_i2c_driver(yas5xx_driver); |
| |
| MODULE_DESCRIPTION("Yamaha YAS53x 3-axis magnetometer driver"); |
| MODULE_AUTHOR("Linus Walleij"); |
| MODULE_LICENSE("GPL v2"); |