18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * MMC35240 - MEMSIC 3-axis Magnetic Sensor 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2015, Intel Corporation. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * IIO driver for MMC35240 (7-bit I2C slave address 0x30). 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * TODO: offset, ACPI, continuous measurement mode, PM 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/i2c.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/regmap.h> 178c2ecf20Sopenharmony_ci#include <linux/acpi.h> 188c2ecf20Sopenharmony_ci#include <linux/pm.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 218c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define MMC35240_DRV_NAME "mmc35240" 248c2ecf20Sopenharmony_ci#define MMC35240_REGMAP_NAME "mmc35240_regmap" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define MMC35240_REG_XOUT_L 0x00 278c2ecf20Sopenharmony_ci#define MMC35240_REG_XOUT_H 0x01 288c2ecf20Sopenharmony_ci#define MMC35240_REG_YOUT_L 0x02 298c2ecf20Sopenharmony_ci#define MMC35240_REG_YOUT_H 0x03 308c2ecf20Sopenharmony_ci#define MMC35240_REG_ZOUT_L 0x04 318c2ecf20Sopenharmony_ci#define MMC35240_REG_ZOUT_H 0x05 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define MMC35240_REG_STATUS 0x06 348c2ecf20Sopenharmony_ci#define MMC35240_REG_CTRL0 0x07 358c2ecf20Sopenharmony_ci#define MMC35240_REG_CTRL1 0x08 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define MMC35240_REG_ID 0x20 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define MMC35240_STATUS_MEAS_DONE_BIT BIT(0) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define MMC35240_CTRL0_REFILL_BIT BIT(7) 428c2ecf20Sopenharmony_ci#define MMC35240_CTRL0_RESET_BIT BIT(6) 438c2ecf20Sopenharmony_ci#define MMC35240_CTRL0_SET_BIT BIT(5) 448c2ecf20Sopenharmony_ci#define MMC35240_CTRL0_CMM_BIT BIT(1) 458c2ecf20Sopenharmony_ci#define MMC35240_CTRL0_TM_BIT BIT(0) 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* output resolution bits */ 488c2ecf20Sopenharmony_ci#define MMC35240_CTRL1_BW0_BIT BIT(0) 498c2ecf20Sopenharmony_ci#define MMC35240_CTRL1_BW1_BIT BIT(1) 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define MMC35240_CTRL1_BW_MASK (MMC35240_CTRL1_BW0_BIT | \ 528c2ecf20Sopenharmony_ci MMC35240_CTRL1_BW1_BIT) 538c2ecf20Sopenharmony_ci#define MMC35240_CTRL1_BW_SHIFT 0 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define MMC35240_WAIT_CHARGE_PUMP 50000 /* us */ 568c2ecf20Sopenharmony_ci#define MMC35240_WAIT_SET_RESET 1000 /* us */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * Memsic OTP process code piece is put here for reference: 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * #define OTP_CONVERT(REG) ((float)((REG) >=32 ? (32 - (REG)) : (REG)) * 0.006 628c2ecf20Sopenharmony_ci * 1) For X axis, the COEFFICIENT is always 1. 638c2ecf20Sopenharmony_ci * 2) For Y axis, the COEFFICIENT is as below: 648c2ecf20Sopenharmony_ci * f_OTP_matrix[4] = OTP_CONVERT(((reg_data[1] & 0x03) << 4) | 658c2ecf20Sopenharmony_ci * (reg_data[2] >> 4)) + 1.0; 668c2ecf20Sopenharmony_ci * 3) For Z axis, the COEFFICIENT is as below: 678c2ecf20Sopenharmony_ci * f_OTP_matrix[8] = (OTP_CONVERT(reg_data[3] & 0x3f) + 1) * 1.35; 688c2ecf20Sopenharmony_ci * We implemented the OTP logic into driver. 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* scale = 1000 here for Y otp */ 728c2ecf20Sopenharmony_ci#define MMC35240_OTP_CONVERT_Y(REG) (((REG) >= 32 ? (32 - (REG)) : (REG)) * 6) 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* 0.6 * 1.35 = 0.81, scale 10000 for Z otp */ 758c2ecf20Sopenharmony_ci#define MMC35240_OTP_CONVERT_Z(REG) (((REG) >= 32 ? (32 - (REG)) : (REG)) * 81) 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define MMC35240_X_COEFF(x) (x) 788c2ecf20Sopenharmony_ci#define MMC35240_Y_COEFF(y) (y + 1000) 798c2ecf20Sopenharmony_ci#define MMC35240_Z_COEFF(z) (z + 13500) 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#define MMC35240_OTP_START_ADDR 0x1B 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cienum mmc35240_resolution { 848c2ecf20Sopenharmony_ci MMC35240_16_BITS_SLOW = 0, /* 7.92 ms */ 858c2ecf20Sopenharmony_ci MMC35240_16_BITS_FAST, /* 4.08 ms */ 868c2ecf20Sopenharmony_ci MMC35240_14_BITS, /* 2.16 ms */ 878c2ecf20Sopenharmony_ci MMC35240_12_BITS, /* 1.20 ms */ 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cienum mmc35240_axis { 918c2ecf20Sopenharmony_ci AXIS_X = 0, 928c2ecf20Sopenharmony_ci AXIS_Y, 938c2ecf20Sopenharmony_ci AXIS_Z, 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic const struct { 978c2ecf20Sopenharmony_ci int sens[3]; /* sensitivity per X, Y, Z axis */ 988c2ecf20Sopenharmony_ci int nfo; /* null field output */ 998c2ecf20Sopenharmony_ci} mmc35240_props_table[] = { 1008c2ecf20Sopenharmony_ci /* 16 bits, 125Hz ODR */ 1018c2ecf20Sopenharmony_ci { 1028c2ecf20Sopenharmony_ci {1024, 1024, 1024}, 1038c2ecf20Sopenharmony_ci 32768, 1048c2ecf20Sopenharmony_ci }, 1058c2ecf20Sopenharmony_ci /* 16 bits, 250Hz ODR */ 1068c2ecf20Sopenharmony_ci { 1078c2ecf20Sopenharmony_ci {1024, 1024, 770}, 1088c2ecf20Sopenharmony_ci 32768, 1098c2ecf20Sopenharmony_ci }, 1108c2ecf20Sopenharmony_ci /* 14 bits, 450Hz ODR */ 1118c2ecf20Sopenharmony_ci { 1128c2ecf20Sopenharmony_ci {256, 256, 193}, 1138c2ecf20Sopenharmony_ci 8192, 1148c2ecf20Sopenharmony_ci }, 1158c2ecf20Sopenharmony_ci /* 12 bits, 800Hz ODR */ 1168c2ecf20Sopenharmony_ci { 1178c2ecf20Sopenharmony_ci {64, 64, 48}, 1188c2ecf20Sopenharmony_ci 2048, 1198c2ecf20Sopenharmony_ci }, 1208c2ecf20Sopenharmony_ci}; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistruct mmc35240_data { 1238c2ecf20Sopenharmony_ci struct i2c_client *client; 1248c2ecf20Sopenharmony_ci struct mutex mutex; 1258c2ecf20Sopenharmony_ci struct regmap *regmap; 1268c2ecf20Sopenharmony_ci enum mmc35240_resolution res; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* OTP compensation */ 1298c2ecf20Sopenharmony_ci int axis_coef[3]; 1308c2ecf20Sopenharmony_ci int axis_scale[3]; 1318c2ecf20Sopenharmony_ci}; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic const struct { 1348c2ecf20Sopenharmony_ci int val; 1358c2ecf20Sopenharmony_ci int val2; 1368c2ecf20Sopenharmony_ci} mmc35240_samp_freq[] = { {1, 500000}, 1378c2ecf20Sopenharmony_ci {13, 0}, 1388c2ecf20Sopenharmony_ci {25, 0}, 1398c2ecf20Sopenharmony_ci {50, 0} }; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1.5 13 25 50"); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci#define MMC35240_CHANNEL(_axis) { \ 1448c2ecf20Sopenharmony_ci .type = IIO_MAGN, \ 1458c2ecf20Sopenharmony_ci .modified = 1, \ 1468c2ecf20Sopenharmony_ci .channel2 = IIO_MOD_ ## _axis, \ 1478c2ecf20Sopenharmony_ci .address = AXIS_ ## _axis, \ 1488c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 1498c2ecf20Sopenharmony_ci .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ 1508c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), \ 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic const struct iio_chan_spec mmc35240_channels[] = { 1548c2ecf20Sopenharmony_ci MMC35240_CHANNEL(X), 1558c2ecf20Sopenharmony_ci MMC35240_CHANNEL(Y), 1568c2ecf20Sopenharmony_ci MMC35240_CHANNEL(Z), 1578c2ecf20Sopenharmony_ci}; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic struct attribute *mmc35240_attributes[] = { 1608c2ecf20Sopenharmony_ci &iio_const_attr_sampling_frequency_available.dev_attr.attr, 1618c2ecf20Sopenharmony_ci NULL 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic const struct attribute_group mmc35240_attribute_group = { 1658c2ecf20Sopenharmony_ci .attrs = mmc35240_attributes, 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic int mmc35240_get_samp_freq_index(struct mmc35240_data *data, 1698c2ecf20Sopenharmony_ci int val, int val2) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci int i; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mmc35240_samp_freq); i++) 1748c2ecf20Sopenharmony_ci if (mmc35240_samp_freq[i].val == val && 1758c2ecf20Sopenharmony_ci mmc35240_samp_freq[i].val2 == val2) 1768c2ecf20Sopenharmony_ci return i; 1778c2ecf20Sopenharmony_ci return -EINVAL; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic int mmc35240_hw_set(struct mmc35240_data *data, bool set) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci int ret; 1838c2ecf20Sopenharmony_ci u8 coil_bit; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* 1868c2ecf20Sopenharmony_ci * Recharge the capacitor at VCAP pin, requested to be issued 1878c2ecf20Sopenharmony_ci * before a SET/RESET command. 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, MMC35240_REG_CTRL0, 1908c2ecf20Sopenharmony_ci MMC35240_CTRL0_REFILL_BIT, 1918c2ecf20Sopenharmony_ci MMC35240_CTRL0_REFILL_BIT); 1928c2ecf20Sopenharmony_ci if (ret < 0) 1938c2ecf20Sopenharmony_ci return ret; 1948c2ecf20Sopenharmony_ci usleep_range(MMC35240_WAIT_CHARGE_PUMP, MMC35240_WAIT_CHARGE_PUMP + 1); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (set) 1978c2ecf20Sopenharmony_ci coil_bit = MMC35240_CTRL0_SET_BIT; 1988c2ecf20Sopenharmony_ci else 1998c2ecf20Sopenharmony_ci coil_bit = MMC35240_CTRL0_RESET_BIT; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return regmap_update_bits(data->regmap, MMC35240_REG_CTRL0, 2028c2ecf20Sopenharmony_ci coil_bit, coil_bit); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic int mmc35240_init(struct mmc35240_data *data) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci int ret, y_convert, z_convert; 2098c2ecf20Sopenharmony_ci unsigned int reg_id; 2108c2ecf20Sopenharmony_ci u8 otp_data[6]; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, MMC35240_REG_ID, ®_id); 2138c2ecf20Sopenharmony_ci if (ret < 0) { 2148c2ecf20Sopenharmony_ci dev_err(&data->client->dev, "Error reading product id\n"); 2158c2ecf20Sopenharmony_ci return ret; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci dev_dbg(&data->client->dev, "MMC35240 chip id %x\n", reg_id); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* 2218c2ecf20Sopenharmony_ci * make sure we restore sensor characteristics, by doing 2228c2ecf20Sopenharmony_ci * a SET/RESET sequence, the axis polarity being naturally 2238c2ecf20Sopenharmony_ci * aligned after RESET 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_ci ret = mmc35240_hw_set(data, true); 2268c2ecf20Sopenharmony_ci if (ret < 0) 2278c2ecf20Sopenharmony_ci return ret; 2288c2ecf20Sopenharmony_ci usleep_range(MMC35240_WAIT_SET_RESET, MMC35240_WAIT_SET_RESET + 1); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci ret = mmc35240_hw_set(data, false); 2318c2ecf20Sopenharmony_ci if (ret < 0) 2328c2ecf20Sopenharmony_ci return ret; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* set default sampling frequency */ 2358c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, MMC35240_REG_CTRL1, 2368c2ecf20Sopenharmony_ci MMC35240_CTRL1_BW_MASK, 2378c2ecf20Sopenharmony_ci data->res << MMC35240_CTRL1_BW_SHIFT); 2388c2ecf20Sopenharmony_ci if (ret < 0) 2398c2ecf20Sopenharmony_ci return ret; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci ret = regmap_bulk_read(data->regmap, MMC35240_OTP_START_ADDR, 2428c2ecf20Sopenharmony_ci otp_data, sizeof(otp_data)); 2438c2ecf20Sopenharmony_ci if (ret < 0) 2448c2ecf20Sopenharmony_ci return ret; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci y_convert = MMC35240_OTP_CONVERT_Y(((otp_data[1] & 0x03) << 4) | 2478c2ecf20Sopenharmony_ci (otp_data[2] >> 4)); 2488c2ecf20Sopenharmony_ci z_convert = MMC35240_OTP_CONVERT_Z(otp_data[3] & 0x3f); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci data->axis_coef[0] = MMC35240_X_COEFF(1); 2518c2ecf20Sopenharmony_ci data->axis_coef[1] = MMC35240_Y_COEFF(y_convert); 2528c2ecf20Sopenharmony_ci data->axis_coef[2] = MMC35240_Z_COEFF(z_convert); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci data->axis_scale[0] = 1; 2558c2ecf20Sopenharmony_ci data->axis_scale[1] = 1000; 2568c2ecf20Sopenharmony_ci data->axis_scale[2] = 10000; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return 0; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic int mmc35240_take_measurement(struct mmc35240_data *data) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci int ret, tries = 100; 2648c2ecf20Sopenharmony_ci unsigned int reg_status; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci ret = regmap_write(data->regmap, MMC35240_REG_CTRL0, 2678c2ecf20Sopenharmony_ci MMC35240_CTRL0_TM_BIT); 2688c2ecf20Sopenharmony_ci if (ret < 0) 2698c2ecf20Sopenharmony_ci return ret; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci while (tries-- > 0) { 2728c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, MMC35240_REG_STATUS, 2738c2ecf20Sopenharmony_ci ®_status); 2748c2ecf20Sopenharmony_ci if (ret < 0) 2758c2ecf20Sopenharmony_ci return ret; 2768c2ecf20Sopenharmony_ci if (reg_status & MMC35240_STATUS_MEAS_DONE_BIT) 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci /* minimum wait time to complete measurement is 10 ms */ 2798c2ecf20Sopenharmony_ci usleep_range(10000, 11000); 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (tries < 0) { 2838c2ecf20Sopenharmony_ci dev_err(&data->client->dev, "data not ready\n"); 2848c2ecf20Sopenharmony_ci return -EIO; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return 0; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic int mmc35240_read_measurement(struct mmc35240_data *data, __le16 buf[3]) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci int ret; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci ret = mmc35240_take_measurement(data); 2958c2ecf20Sopenharmony_ci if (ret < 0) 2968c2ecf20Sopenharmony_ci return ret; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return regmap_bulk_read(data->regmap, MMC35240_REG_XOUT_L, buf, 2998c2ecf20Sopenharmony_ci 3 * sizeof(__le16)); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci/** 3038c2ecf20Sopenharmony_ci * mmc35240_raw_to_mgauss - convert raw readings to milli gauss. Also apply 3048c2ecf20Sopenharmony_ci * compensation for output value. 3058c2ecf20Sopenharmony_ci * 3068c2ecf20Sopenharmony_ci * @data: device private data 3078c2ecf20Sopenharmony_ci * @index: axis index for which we want the conversion 3088c2ecf20Sopenharmony_ci * @buf: raw data to be converted, 2 bytes in little endian format 3098c2ecf20Sopenharmony_ci * @val: compensated output reading (unit is milli gauss) 3108c2ecf20Sopenharmony_ci * 3118c2ecf20Sopenharmony_ci * Returns: 0 in case of success, -EINVAL when @index is not valid 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_cistatic int mmc35240_raw_to_mgauss(struct mmc35240_data *data, int index, 3148c2ecf20Sopenharmony_ci __le16 buf[], int *val) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci int raw[3]; 3178c2ecf20Sopenharmony_ci int sens[3]; 3188c2ecf20Sopenharmony_ci int nfo; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci raw[AXIS_X] = le16_to_cpu(buf[AXIS_X]); 3218c2ecf20Sopenharmony_ci raw[AXIS_Y] = le16_to_cpu(buf[AXIS_Y]); 3228c2ecf20Sopenharmony_ci raw[AXIS_Z] = le16_to_cpu(buf[AXIS_Z]); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci sens[AXIS_X] = mmc35240_props_table[data->res].sens[AXIS_X]; 3258c2ecf20Sopenharmony_ci sens[AXIS_Y] = mmc35240_props_table[data->res].sens[AXIS_Y]; 3268c2ecf20Sopenharmony_ci sens[AXIS_Z] = mmc35240_props_table[data->res].sens[AXIS_Z]; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci nfo = mmc35240_props_table[data->res].nfo; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci switch (index) { 3318c2ecf20Sopenharmony_ci case AXIS_X: 3328c2ecf20Sopenharmony_ci *val = (raw[AXIS_X] - nfo) * 1000 / sens[AXIS_X]; 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci case AXIS_Y: 3358c2ecf20Sopenharmony_ci *val = (raw[AXIS_Y] - nfo) * 1000 / sens[AXIS_Y] - 3368c2ecf20Sopenharmony_ci (raw[AXIS_Z] - nfo) * 1000 / sens[AXIS_Z]; 3378c2ecf20Sopenharmony_ci break; 3388c2ecf20Sopenharmony_ci case AXIS_Z: 3398c2ecf20Sopenharmony_ci *val = (raw[AXIS_Y] - nfo) * 1000 / sens[AXIS_Y] + 3408c2ecf20Sopenharmony_ci (raw[AXIS_Z] - nfo) * 1000 / sens[AXIS_Z]; 3418c2ecf20Sopenharmony_ci break; 3428c2ecf20Sopenharmony_ci default: 3438c2ecf20Sopenharmony_ci return -EINVAL; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci /* apply OTP compensation */ 3468c2ecf20Sopenharmony_ci *val = (*val) * data->axis_coef[index] / data->axis_scale[index]; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return 0; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic int mmc35240_read_raw(struct iio_dev *indio_dev, 3528c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, int *val, 3538c2ecf20Sopenharmony_ci int *val2, long mask) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci struct mmc35240_data *data = iio_priv(indio_dev); 3568c2ecf20Sopenharmony_ci int ret, i; 3578c2ecf20Sopenharmony_ci unsigned int reg; 3588c2ecf20Sopenharmony_ci __le16 buf[3]; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci switch (mask) { 3618c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 3628c2ecf20Sopenharmony_ci mutex_lock(&data->mutex); 3638c2ecf20Sopenharmony_ci ret = mmc35240_read_measurement(data, buf); 3648c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 3658c2ecf20Sopenharmony_ci if (ret < 0) 3668c2ecf20Sopenharmony_ci return ret; 3678c2ecf20Sopenharmony_ci ret = mmc35240_raw_to_mgauss(data, chan->address, buf, val); 3688c2ecf20Sopenharmony_ci if (ret < 0) 3698c2ecf20Sopenharmony_ci return ret; 3708c2ecf20Sopenharmony_ci return IIO_VAL_INT; 3718c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 3728c2ecf20Sopenharmony_ci *val = 0; 3738c2ecf20Sopenharmony_ci *val2 = 1000; 3748c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 3758c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: 3768c2ecf20Sopenharmony_ci mutex_lock(&data->mutex); 3778c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, MMC35240_REG_CTRL1, ®); 3788c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 3798c2ecf20Sopenharmony_ci if (ret < 0) 3808c2ecf20Sopenharmony_ci return ret; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci i = (reg & MMC35240_CTRL1_BW_MASK) >> MMC35240_CTRL1_BW_SHIFT; 3838c2ecf20Sopenharmony_ci if (i < 0 || i >= ARRAY_SIZE(mmc35240_samp_freq)) 3848c2ecf20Sopenharmony_ci return -EINVAL; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci *val = mmc35240_samp_freq[i].val; 3878c2ecf20Sopenharmony_ci *val2 = mmc35240_samp_freq[i].val2; 3888c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 3898c2ecf20Sopenharmony_ci default: 3908c2ecf20Sopenharmony_ci return -EINVAL; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic int mmc35240_write_raw(struct iio_dev *indio_dev, 3958c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, int val, 3968c2ecf20Sopenharmony_ci int val2, long mask) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct mmc35240_data *data = iio_priv(indio_dev); 3998c2ecf20Sopenharmony_ci int i, ret; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci switch (mask) { 4028c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: 4038c2ecf20Sopenharmony_ci i = mmc35240_get_samp_freq_index(data, val, val2); 4048c2ecf20Sopenharmony_ci if (i < 0) 4058c2ecf20Sopenharmony_ci return -EINVAL; 4068c2ecf20Sopenharmony_ci mutex_lock(&data->mutex); 4078c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, MMC35240_REG_CTRL1, 4088c2ecf20Sopenharmony_ci MMC35240_CTRL1_BW_MASK, 4098c2ecf20Sopenharmony_ci i << MMC35240_CTRL1_BW_SHIFT); 4108c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 4118c2ecf20Sopenharmony_ci return ret; 4128c2ecf20Sopenharmony_ci default: 4138c2ecf20Sopenharmony_ci return -EINVAL; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic const struct iio_info mmc35240_info = { 4188c2ecf20Sopenharmony_ci .read_raw = mmc35240_read_raw, 4198c2ecf20Sopenharmony_ci .write_raw = mmc35240_write_raw, 4208c2ecf20Sopenharmony_ci .attrs = &mmc35240_attribute_group, 4218c2ecf20Sopenharmony_ci}; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic bool mmc35240_is_writeable_reg(struct device *dev, unsigned int reg) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci switch (reg) { 4268c2ecf20Sopenharmony_ci case MMC35240_REG_CTRL0: 4278c2ecf20Sopenharmony_ci case MMC35240_REG_CTRL1: 4288c2ecf20Sopenharmony_ci return true; 4298c2ecf20Sopenharmony_ci default: 4308c2ecf20Sopenharmony_ci return false; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic bool mmc35240_is_readable_reg(struct device *dev, unsigned int reg) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci switch (reg) { 4378c2ecf20Sopenharmony_ci case MMC35240_REG_XOUT_L: 4388c2ecf20Sopenharmony_ci case MMC35240_REG_XOUT_H: 4398c2ecf20Sopenharmony_ci case MMC35240_REG_YOUT_L: 4408c2ecf20Sopenharmony_ci case MMC35240_REG_YOUT_H: 4418c2ecf20Sopenharmony_ci case MMC35240_REG_ZOUT_L: 4428c2ecf20Sopenharmony_ci case MMC35240_REG_ZOUT_H: 4438c2ecf20Sopenharmony_ci case MMC35240_REG_STATUS: 4448c2ecf20Sopenharmony_ci case MMC35240_REG_ID: 4458c2ecf20Sopenharmony_ci return true; 4468c2ecf20Sopenharmony_ci default: 4478c2ecf20Sopenharmony_ci return false; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic bool mmc35240_is_volatile_reg(struct device *dev, unsigned int reg) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci switch (reg) { 4548c2ecf20Sopenharmony_ci case MMC35240_REG_CTRL0: 4558c2ecf20Sopenharmony_ci case MMC35240_REG_CTRL1: 4568c2ecf20Sopenharmony_ci return false; 4578c2ecf20Sopenharmony_ci default: 4588c2ecf20Sopenharmony_ci return true; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic const struct reg_default mmc35240_reg_defaults[] = { 4638c2ecf20Sopenharmony_ci { MMC35240_REG_CTRL0, 0x00 }, 4648c2ecf20Sopenharmony_ci { MMC35240_REG_CTRL1, 0x00 }, 4658c2ecf20Sopenharmony_ci}; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic const struct regmap_config mmc35240_regmap_config = { 4688c2ecf20Sopenharmony_ci .name = MMC35240_REGMAP_NAME, 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci .reg_bits = 8, 4718c2ecf20Sopenharmony_ci .val_bits = 8, 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci .max_register = MMC35240_REG_ID, 4748c2ecf20Sopenharmony_ci .cache_type = REGCACHE_FLAT, 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci .writeable_reg = mmc35240_is_writeable_reg, 4778c2ecf20Sopenharmony_ci .readable_reg = mmc35240_is_readable_reg, 4788c2ecf20Sopenharmony_ci .volatile_reg = mmc35240_is_volatile_reg, 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci .reg_defaults = mmc35240_reg_defaults, 4818c2ecf20Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(mmc35240_reg_defaults), 4828c2ecf20Sopenharmony_ci}; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic int mmc35240_probe(struct i2c_client *client, 4858c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci struct mmc35240_data *data; 4888c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 4898c2ecf20Sopenharmony_ci struct regmap *regmap; 4908c2ecf20Sopenharmony_ci int ret; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 4938c2ecf20Sopenharmony_ci if (!indio_dev) 4948c2ecf20Sopenharmony_ci return -ENOMEM; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci regmap = devm_regmap_init_i2c(client, &mmc35240_regmap_config); 4978c2ecf20Sopenharmony_ci if (IS_ERR(regmap)) { 4988c2ecf20Sopenharmony_ci dev_err(&client->dev, "regmap initialization failed\n"); 4998c2ecf20Sopenharmony_ci return PTR_ERR(regmap); 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 5038c2ecf20Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 5048c2ecf20Sopenharmony_ci data->client = client; 5058c2ecf20Sopenharmony_ci data->regmap = regmap; 5068c2ecf20Sopenharmony_ci data->res = MMC35240_16_BITS_SLOW; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci mutex_init(&data->mutex); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci indio_dev->info = &mmc35240_info; 5118c2ecf20Sopenharmony_ci indio_dev->name = MMC35240_DRV_NAME; 5128c2ecf20Sopenharmony_ci indio_dev->channels = mmc35240_channels; 5138c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(mmc35240_channels); 5148c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci ret = mmc35240_init(data); 5178c2ecf20Sopenharmony_ci if (ret < 0) { 5188c2ecf20Sopenharmony_ci dev_err(&client->dev, "mmc35240 chip init failed\n"); 5198c2ecf20Sopenharmony_ci return ret; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci return devm_iio_device_register(&client->dev, indio_dev); 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5258c2ecf20Sopenharmony_cistatic int mmc35240_suspend(struct device *dev) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 5288c2ecf20Sopenharmony_ci struct mmc35240_data *data = iio_priv(indio_dev); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci regcache_cache_only(data->regmap, true); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return 0; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic int mmc35240_resume(struct device *dev) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 5388c2ecf20Sopenharmony_ci struct mmc35240_data *data = iio_priv(indio_dev); 5398c2ecf20Sopenharmony_ci int ret; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci regcache_mark_dirty(data->regmap); 5428c2ecf20Sopenharmony_ci ret = regcache_sync_region(data->regmap, MMC35240_REG_CTRL0, 5438c2ecf20Sopenharmony_ci MMC35240_REG_CTRL1); 5448c2ecf20Sopenharmony_ci if (ret < 0) 5458c2ecf20Sopenharmony_ci dev_err(dev, "Failed to restore control registers\n"); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci regcache_cache_only(data->regmap, false); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci return 0; 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci#endif 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic const struct dev_pm_ops mmc35240_pm_ops = { 5548c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(mmc35240_suspend, mmc35240_resume) 5558c2ecf20Sopenharmony_ci}; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic const struct of_device_id mmc35240_of_match[] = { 5588c2ecf20Sopenharmony_ci { .compatible = "memsic,mmc35240", }, 5598c2ecf20Sopenharmony_ci { } 5608c2ecf20Sopenharmony_ci}; 5618c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mmc35240_of_match); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic const struct acpi_device_id mmc35240_acpi_match[] = { 5648c2ecf20Sopenharmony_ci {"MMC35240", 0}, 5658c2ecf20Sopenharmony_ci { }, 5668c2ecf20Sopenharmony_ci}; 5678c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, mmc35240_acpi_match); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic const struct i2c_device_id mmc35240_id[] = { 5708c2ecf20Sopenharmony_ci {"mmc35240", 0}, 5718c2ecf20Sopenharmony_ci {} 5728c2ecf20Sopenharmony_ci}; 5738c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mmc35240_id); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic struct i2c_driver mmc35240_driver = { 5768c2ecf20Sopenharmony_ci .driver = { 5778c2ecf20Sopenharmony_ci .name = MMC35240_DRV_NAME, 5788c2ecf20Sopenharmony_ci .of_match_table = mmc35240_of_match, 5798c2ecf20Sopenharmony_ci .pm = &mmc35240_pm_ops, 5808c2ecf20Sopenharmony_ci .acpi_match_table = ACPI_PTR(mmc35240_acpi_match), 5818c2ecf20Sopenharmony_ci }, 5828c2ecf20Sopenharmony_ci .probe = mmc35240_probe, 5838c2ecf20Sopenharmony_ci .id_table = mmc35240_id, 5848c2ecf20Sopenharmony_ci}; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_cimodule_i2c_driver(mmc35240_driver); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ciMODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>"); 5898c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MEMSIC MMC35240 magnetic sensor driver"); 5908c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 591