18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Bosch BMC150 three-axis magnetic field sensor driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2015, Intel Corporation. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This code is based on bmm050_api.c authored by contact@bosch.sensortec.com: 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * (C) Copyright 2011~2014 Bosch Sensortec GmbH All Rights Reserved 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/i2c.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/acpi.h> 188c2ecf20Sopenharmony_ci#include <linux/pm.h> 198c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 208c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 218c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 228c2ecf20Sopenharmony_ci#include <linux/iio/buffer.h> 238c2ecf20Sopenharmony_ci#include <linux/iio/events.h> 248c2ecf20Sopenharmony_ci#include <linux/iio/trigger.h> 258c2ecf20Sopenharmony_ci#include <linux/iio/trigger_consumer.h> 268c2ecf20Sopenharmony_ci#include <linux/iio/triggered_buffer.h> 278c2ecf20Sopenharmony_ci#include <linux/regmap.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "bmc150_magn.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define BMC150_MAGN_DRV_NAME "bmc150_magn" 328c2ecf20Sopenharmony_ci#define BMC150_MAGN_IRQ_NAME "bmc150_magn_event" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define BMC150_MAGN_REG_CHIP_ID 0x40 358c2ecf20Sopenharmony_ci#define BMC150_MAGN_CHIP_ID_VAL 0x32 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define BMC150_MAGN_REG_X_L 0x42 388c2ecf20Sopenharmony_ci#define BMC150_MAGN_REG_X_M 0x43 398c2ecf20Sopenharmony_ci#define BMC150_MAGN_REG_Y_L 0x44 408c2ecf20Sopenharmony_ci#define BMC150_MAGN_REG_Y_M 0x45 418c2ecf20Sopenharmony_ci#define BMC150_MAGN_SHIFT_XY_L 3 428c2ecf20Sopenharmony_ci#define BMC150_MAGN_REG_Z_L 0x46 438c2ecf20Sopenharmony_ci#define BMC150_MAGN_REG_Z_M 0x47 448c2ecf20Sopenharmony_ci#define BMC150_MAGN_SHIFT_Z_L 1 458c2ecf20Sopenharmony_ci#define BMC150_MAGN_REG_RHALL_L 0x48 468c2ecf20Sopenharmony_ci#define BMC150_MAGN_REG_RHALL_M 0x49 478c2ecf20Sopenharmony_ci#define BMC150_MAGN_SHIFT_RHALL_L 2 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define BMC150_MAGN_REG_INT_STATUS 0x4A 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define BMC150_MAGN_REG_POWER 0x4B 528c2ecf20Sopenharmony_ci#define BMC150_MAGN_MASK_POWER_CTL BIT(0) 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define BMC150_MAGN_REG_OPMODE_ODR 0x4C 558c2ecf20Sopenharmony_ci#define BMC150_MAGN_MASK_OPMODE GENMASK(2, 1) 568c2ecf20Sopenharmony_ci#define BMC150_MAGN_SHIFT_OPMODE 1 578c2ecf20Sopenharmony_ci#define BMC150_MAGN_MODE_NORMAL 0x00 588c2ecf20Sopenharmony_ci#define BMC150_MAGN_MODE_FORCED 0x01 598c2ecf20Sopenharmony_ci#define BMC150_MAGN_MODE_SLEEP 0x03 608c2ecf20Sopenharmony_ci#define BMC150_MAGN_MASK_ODR GENMASK(5, 3) 618c2ecf20Sopenharmony_ci#define BMC150_MAGN_SHIFT_ODR 3 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define BMC150_MAGN_REG_INT 0x4D 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define BMC150_MAGN_REG_INT_DRDY 0x4E 668c2ecf20Sopenharmony_ci#define BMC150_MAGN_MASK_DRDY_EN BIT(7) 678c2ecf20Sopenharmony_ci#define BMC150_MAGN_SHIFT_DRDY_EN 7 688c2ecf20Sopenharmony_ci#define BMC150_MAGN_MASK_DRDY_INT3 BIT(6) 698c2ecf20Sopenharmony_ci#define BMC150_MAGN_MASK_DRDY_Z_EN BIT(5) 708c2ecf20Sopenharmony_ci#define BMC150_MAGN_MASK_DRDY_Y_EN BIT(4) 718c2ecf20Sopenharmony_ci#define BMC150_MAGN_MASK_DRDY_X_EN BIT(3) 728c2ecf20Sopenharmony_ci#define BMC150_MAGN_MASK_DRDY_DR_POLARITY BIT(2) 738c2ecf20Sopenharmony_ci#define BMC150_MAGN_MASK_DRDY_LATCHING BIT(1) 748c2ecf20Sopenharmony_ci#define BMC150_MAGN_MASK_DRDY_INT3_POLARITY BIT(0) 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#define BMC150_MAGN_REG_LOW_THRESH 0x4F 778c2ecf20Sopenharmony_ci#define BMC150_MAGN_REG_HIGH_THRESH 0x50 788c2ecf20Sopenharmony_ci#define BMC150_MAGN_REG_REP_XY 0x51 798c2ecf20Sopenharmony_ci#define BMC150_MAGN_REG_REP_Z 0x52 808c2ecf20Sopenharmony_ci#define BMC150_MAGN_REG_REP_DATAMASK GENMASK(7, 0) 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#define BMC150_MAGN_REG_TRIM_START 0x5D 838c2ecf20Sopenharmony_ci#define BMC150_MAGN_REG_TRIM_END 0x71 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#define BMC150_MAGN_XY_OVERFLOW_VAL -4096 868c2ecf20Sopenharmony_ci#define BMC150_MAGN_Z_OVERFLOW_VAL -16384 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* Time from SUSPEND to SLEEP */ 898c2ecf20Sopenharmony_ci#define BMC150_MAGN_START_UP_TIME_MS 3 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#define BMC150_MAGN_AUTO_SUSPEND_DELAY_MS 2000 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#define BMC150_MAGN_REGVAL_TO_REPXY(regval) (((regval) * 2) + 1) 948c2ecf20Sopenharmony_ci#define BMC150_MAGN_REGVAL_TO_REPZ(regval) ((regval) + 1) 958c2ecf20Sopenharmony_ci#define BMC150_MAGN_REPXY_TO_REGVAL(rep) (((rep) - 1) / 2) 968c2ecf20Sopenharmony_ci#define BMC150_MAGN_REPZ_TO_REGVAL(rep) ((rep) - 1) 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cienum bmc150_magn_axis { 998c2ecf20Sopenharmony_ci AXIS_X, 1008c2ecf20Sopenharmony_ci AXIS_Y, 1018c2ecf20Sopenharmony_ci AXIS_Z, 1028c2ecf20Sopenharmony_ci RHALL, 1038c2ecf20Sopenharmony_ci AXIS_XYZ_MAX = RHALL, 1048c2ecf20Sopenharmony_ci AXIS_XYZR_MAX, 1058c2ecf20Sopenharmony_ci}; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cienum bmc150_magn_power_modes { 1088c2ecf20Sopenharmony_ci BMC150_MAGN_POWER_MODE_SUSPEND, 1098c2ecf20Sopenharmony_ci BMC150_MAGN_POWER_MODE_SLEEP, 1108c2ecf20Sopenharmony_ci BMC150_MAGN_POWER_MODE_NORMAL, 1118c2ecf20Sopenharmony_ci}; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistruct bmc150_magn_trim_regs { 1148c2ecf20Sopenharmony_ci s8 x1; 1158c2ecf20Sopenharmony_ci s8 y1; 1168c2ecf20Sopenharmony_ci __le16 reserved1; 1178c2ecf20Sopenharmony_ci u8 reserved2; 1188c2ecf20Sopenharmony_ci __le16 z4; 1198c2ecf20Sopenharmony_ci s8 x2; 1208c2ecf20Sopenharmony_ci s8 y2; 1218c2ecf20Sopenharmony_ci __le16 reserved3; 1228c2ecf20Sopenharmony_ci __le16 z2; 1238c2ecf20Sopenharmony_ci __le16 z1; 1248c2ecf20Sopenharmony_ci __le16 xyz1; 1258c2ecf20Sopenharmony_ci __le16 z3; 1268c2ecf20Sopenharmony_ci s8 xy2; 1278c2ecf20Sopenharmony_ci u8 xy1; 1288c2ecf20Sopenharmony_ci} __packed; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistruct bmc150_magn_data { 1318c2ecf20Sopenharmony_ci struct device *dev; 1328c2ecf20Sopenharmony_ci /* 1338c2ecf20Sopenharmony_ci * 1. Protect this structure. 1348c2ecf20Sopenharmony_ci * 2. Serialize sequences that power on/off the device and access HW. 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_ci struct mutex mutex; 1378c2ecf20Sopenharmony_ci struct regmap *regmap; 1388c2ecf20Sopenharmony_ci struct iio_mount_matrix orientation; 1398c2ecf20Sopenharmony_ci /* Ensure timestamp is naturally aligned */ 1408c2ecf20Sopenharmony_ci struct { 1418c2ecf20Sopenharmony_ci s32 chans[3]; 1428c2ecf20Sopenharmony_ci s64 timestamp __aligned(8); 1438c2ecf20Sopenharmony_ci } scan; 1448c2ecf20Sopenharmony_ci struct iio_trigger *dready_trig; 1458c2ecf20Sopenharmony_ci bool dready_trigger_on; 1468c2ecf20Sopenharmony_ci int max_odr; 1478c2ecf20Sopenharmony_ci int irq; 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic const struct { 1518c2ecf20Sopenharmony_ci int freq; 1528c2ecf20Sopenharmony_ci u8 reg_val; 1538c2ecf20Sopenharmony_ci} bmc150_magn_samp_freq_table[] = { {2, 0x01}, 1548c2ecf20Sopenharmony_ci {6, 0x02}, 1558c2ecf20Sopenharmony_ci {8, 0x03}, 1568c2ecf20Sopenharmony_ci {10, 0x00}, 1578c2ecf20Sopenharmony_ci {15, 0x04}, 1588c2ecf20Sopenharmony_ci {20, 0x05}, 1598c2ecf20Sopenharmony_ci {25, 0x06}, 1608c2ecf20Sopenharmony_ci {30, 0x07} }; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cienum bmc150_magn_presets { 1638c2ecf20Sopenharmony_ci LOW_POWER_PRESET, 1648c2ecf20Sopenharmony_ci REGULAR_PRESET, 1658c2ecf20Sopenharmony_ci ENHANCED_REGULAR_PRESET, 1668c2ecf20Sopenharmony_ci HIGH_ACCURACY_PRESET 1678c2ecf20Sopenharmony_ci}; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic const struct bmc150_magn_preset { 1708c2ecf20Sopenharmony_ci u8 rep_xy; 1718c2ecf20Sopenharmony_ci u8 rep_z; 1728c2ecf20Sopenharmony_ci u8 odr; 1738c2ecf20Sopenharmony_ci} bmc150_magn_presets_table[] = { 1748c2ecf20Sopenharmony_ci [LOW_POWER_PRESET] = {3, 3, 10}, 1758c2ecf20Sopenharmony_ci [REGULAR_PRESET] = {9, 15, 10}, 1768c2ecf20Sopenharmony_ci [ENHANCED_REGULAR_PRESET] = {15, 27, 10}, 1778c2ecf20Sopenharmony_ci [HIGH_ACCURACY_PRESET] = {47, 83, 20}, 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci#define BMC150_MAGN_DEFAULT_PRESET REGULAR_PRESET 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic bool bmc150_magn_is_writeable_reg(struct device *dev, unsigned int reg) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci switch (reg) { 1858c2ecf20Sopenharmony_ci case BMC150_MAGN_REG_POWER: 1868c2ecf20Sopenharmony_ci case BMC150_MAGN_REG_OPMODE_ODR: 1878c2ecf20Sopenharmony_ci case BMC150_MAGN_REG_INT: 1888c2ecf20Sopenharmony_ci case BMC150_MAGN_REG_INT_DRDY: 1898c2ecf20Sopenharmony_ci case BMC150_MAGN_REG_LOW_THRESH: 1908c2ecf20Sopenharmony_ci case BMC150_MAGN_REG_HIGH_THRESH: 1918c2ecf20Sopenharmony_ci case BMC150_MAGN_REG_REP_XY: 1928c2ecf20Sopenharmony_ci case BMC150_MAGN_REG_REP_Z: 1938c2ecf20Sopenharmony_ci return true; 1948c2ecf20Sopenharmony_ci default: 1958c2ecf20Sopenharmony_ci return false; 1968c2ecf20Sopenharmony_ci }; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic bool bmc150_magn_is_volatile_reg(struct device *dev, unsigned int reg) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci switch (reg) { 2028c2ecf20Sopenharmony_ci case BMC150_MAGN_REG_X_L: 2038c2ecf20Sopenharmony_ci case BMC150_MAGN_REG_X_M: 2048c2ecf20Sopenharmony_ci case BMC150_MAGN_REG_Y_L: 2058c2ecf20Sopenharmony_ci case BMC150_MAGN_REG_Y_M: 2068c2ecf20Sopenharmony_ci case BMC150_MAGN_REG_Z_L: 2078c2ecf20Sopenharmony_ci case BMC150_MAGN_REG_Z_M: 2088c2ecf20Sopenharmony_ci case BMC150_MAGN_REG_RHALL_L: 2098c2ecf20Sopenharmony_ci case BMC150_MAGN_REG_RHALL_M: 2108c2ecf20Sopenharmony_ci case BMC150_MAGN_REG_INT_STATUS: 2118c2ecf20Sopenharmony_ci return true; 2128c2ecf20Sopenharmony_ci default: 2138c2ecf20Sopenharmony_ci return false; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ciconst struct regmap_config bmc150_magn_regmap_config = { 2188c2ecf20Sopenharmony_ci .reg_bits = 8, 2198c2ecf20Sopenharmony_ci .val_bits = 8, 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci .max_register = BMC150_MAGN_REG_TRIM_END, 2228c2ecf20Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci .writeable_reg = bmc150_magn_is_writeable_reg, 2258c2ecf20Sopenharmony_ci .volatile_reg = bmc150_magn_is_volatile_reg, 2268c2ecf20Sopenharmony_ci}; 2278c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bmc150_magn_regmap_config); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int bmc150_magn_set_power_mode(struct bmc150_magn_data *data, 2308c2ecf20Sopenharmony_ci enum bmc150_magn_power_modes mode, 2318c2ecf20Sopenharmony_ci bool state) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci int ret; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci switch (mode) { 2368c2ecf20Sopenharmony_ci case BMC150_MAGN_POWER_MODE_SUSPEND: 2378c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, BMC150_MAGN_REG_POWER, 2388c2ecf20Sopenharmony_ci BMC150_MAGN_MASK_POWER_CTL, !state); 2398c2ecf20Sopenharmony_ci if (ret < 0) 2408c2ecf20Sopenharmony_ci return ret; 2418c2ecf20Sopenharmony_ci usleep_range(BMC150_MAGN_START_UP_TIME_MS * 1000, 20000); 2428c2ecf20Sopenharmony_ci return 0; 2438c2ecf20Sopenharmony_ci case BMC150_MAGN_POWER_MODE_SLEEP: 2448c2ecf20Sopenharmony_ci return regmap_update_bits(data->regmap, 2458c2ecf20Sopenharmony_ci BMC150_MAGN_REG_OPMODE_ODR, 2468c2ecf20Sopenharmony_ci BMC150_MAGN_MASK_OPMODE, 2478c2ecf20Sopenharmony_ci BMC150_MAGN_MODE_SLEEP << 2488c2ecf20Sopenharmony_ci BMC150_MAGN_SHIFT_OPMODE); 2498c2ecf20Sopenharmony_ci case BMC150_MAGN_POWER_MODE_NORMAL: 2508c2ecf20Sopenharmony_ci return regmap_update_bits(data->regmap, 2518c2ecf20Sopenharmony_ci BMC150_MAGN_REG_OPMODE_ODR, 2528c2ecf20Sopenharmony_ci BMC150_MAGN_MASK_OPMODE, 2538c2ecf20Sopenharmony_ci BMC150_MAGN_MODE_NORMAL << 2548c2ecf20Sopenharmony_ci BMC150_MAGN_SHIFT_OPMODE); 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return -EINVAL; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic int bmc150_magn_set_power_state(struct bmc150_magn_data *data, bool on) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 2638c2ecf20Sopenharmony_ci int ret; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (on) { 2668c2ecf20Sopenharmony_ci ret = pm_runtime_resume_and_get(data->dev); 2678c2ecf20Sopenharmony_ci } else { 2688c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(data->dev); 2698c2ecf20Sopenharmony_ci ret = pm_runtime_put_autosuspend(data->dev); 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (ret < 0) { 2738c2ecf20Sopenharmony_ci dev_err(data->dev, 2748c2ecf20Sopenharmony_ci "failed to change power state to %d\n", on); 2758c2ecf20Sopenharmony_ci return ret; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci#endif 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci return 0; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic int bmc150_magn_get_odr(struct bmc150_magn_data *data, int *val) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci int ret, reg_val; 2858c2ecf20Sopenharmony_ci u8 i, odr_val; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, BMC150_MAGN_REG_OPMODE_ODR, ®_val); 2888c2ecf20Sopenharmony_ci if (ret < 0) 2898c2ecf20Sopenharmony_ci return ret; 2908c2ecf20Sopenharmony_ci odr_val = (reg_val & BMC150_MAGN_MASK_ODR) >> BMC150_MAGN_SHIFT_ODR; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(bmc150_magn_samp_freq_table); i++) 2938c2ecf20Sopenharmony_ci if (bmc150_magn_samp_freq_table[i].reg_val == odr_val) { 2948c2ecf20Sopenharmony_ci *val = bmc150_magn_samp_freq_table[i].freq; 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return -EINVAL; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic int bmc150_magn_set_odr(struct bmc150_magn_data *data, int val) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci int ret; 3048c2ecf20Sopenharmony_ci u8 i; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(bmc150_magn_samp_freq_table); i++) { 3078c2ecf20Sopenharmony_ci if (bmc150_magn_samp_freq_table[i].freq == val) { 3088c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, 3098c2ecf20Sopenharmony_ci BMC150_MAGN_REG_OPMODE_ODR, 3108c2ecf20Sopenharmony_ci BMC150_MAGN_MASK_ODR, 3118c2ecf20Sopenharmony_ci bmc150_magn_samp_freq_table[i]. 3128c2ecf20Sopenharmony_ci reg_val << 3138c2ecf20Sopenharmony_ci BMC150_MAGN_SHIFT_ODR); 3148c2ecf20Sopenharmony_ci if (ret < 0) 3158c2ecf20Sopenharmony_ci return ret; 3168c2ecf20Sopenharmony_ci return 0; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci return -EINVAL; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic int bmc150_magn_set_max_odr(struct bmc150_magn_data *data, int rep_xy, 3248c2ecf20Sopenharmony_ci int rep_z, int odr) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci int ret, reg_val, max_odr; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (rep_xy <= 0) { 3298c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, BMC150_MAGN_REG_REP_XY, 3308c2ecf20Sopenharmony_ci ®_val); 3318c2ecf20Sopenharmony_ci if (ret < 0) 3328c2ecf20Sopenharmony_ci return ret; 3338c2ecf20Sopenharmony_ci rep_xy = BMC150_MAGN_REGVAL_TO_REPXY(reg_val); 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci if (rep_z <= 0) { 3368c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, BMC150_MAGN_REG_REP_Z, 3378c2ecf20Sopenharmony_ci ®_val); 3388c2ecf20Sopenharmony_ci if (ret < 0) 3398c2ecf20Sopenharmony_ci return ret; 3408c2ecf20Sopenharmony_ci rep_z = BMC150_MAGN_REGVAL_TO_REPZ(reg_val); 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci if (odr <= 0) { 3438c2ecf20Sopenharmony_ci ret = bmc150_magn_get_odr(data, &odr); 3448c2ecf20Sopenharmony_ci if (ret < 0) 3458c2ecf20Sopenharmony_ci return ret; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci /* the maximum selectable read-out frequency from datasheet */ 3488c2ecf20Sopenharmony_ci max_odr = 1000000 / (145 * rep_xy + 500 * rep_z + 980); 3498c2ecf20Sopenharmony_ci if (odr > max_odr) { 3508c2ecf20Sopenharmony_ci dev_err(data->dev, 3518c2ecf20Sopenharmony_ci "Can't set oversampling with sampling freq %d\n", 3528c2ecf20Sopenharmony_ci odr); 3538c2ecf20Sopenharmony_ci return -EINVAL; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci data->max_odr = max_odr; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci return 0; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic s32 bmc150_magn_compensate_x(struct bmc150_magn_trim_regs *tregs, s16 x, 3618c2ecf20Sopenharmony_ci u16 rhall) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci s16 val; 3648c2ecf20Sopenharmony_ci u16 xyz1 = le16_to_cpu(tregs->xyz1); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (x == BMC150_MAGN_XY_OVERFLOW_VAL) 3678c2ecf20Sopenharmony_ci return S32_MIN; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (!rhall) 3708c2ecf20Sopenharmony_ci rhall = xyz1; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci val = ((s16)(((u16)((((s32)xyz1) << 14) / rhall)) - ((u16)0x4000))); 3738c2ecf20Sopenharmony_ci val = ((s16)((((s32)x) * ((((((((s32)tregs->xy2) * ((((s32)val) * 3748c2ecf20Sopenharmony_ci ((s32)val)) >> 7)) + (((s32)val) * 3758c2ecf20Sopenharmony_ci ((s32)(((s16)tregs->xy1) << 7)))) >> 9) + ((s32)0x100000)) * 3768c2ecf20Sopenharmony_ci ((s32)(((s16)tregs->x2) + ((s16)0xA0)))) >> 12)) >> 13)) + 3778c2ecf20Sopenharmony_ci (((s16)tregs->x1) << 3); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci return (s32)val; 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic s32 bmc150_magn_compensate_y(struct bmc150_magn_trim_regs *tregs, s16 y, 3838c2ecf20Sopenharmony_ci u16 rhall) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci s16 val; 3868c2ecf20Sopenharmony_ci u16 xyz1 = le16_to_cpu(tregs->xyz1); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (y == BMC150_MAGN_XY_OVERFLOW_VAL) 3898c2ecf20Sopenharmony_ci return S32_MIN; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (!rhall) 3928c2ecf20Sopenharmony_ci rhall = xyz1; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci val = ((s16)(((u16)((((s32)xyz1) << 14) / rhall)) - ((u16)0x4000))); 3958c2ecf20Sopenharmony_ci val = ((s16)((((s32)y) * ((((((((s32)tregs->xy2) * ((((s32)val) * 3968c2ecf20Sopenharmony_ci ((s32)val)) >> 7)) + (((s32)val) * 3978c2ecf20Sopenharmony_ci ((s32)(((s16)tregs->xy1) << 7)))) >> 9) + ((s32)0x100000)) * 3988c2ecf20Sopenharmony_ci ((s32)(((s16)tregs->y2) + ((s16)0xA0)))) >> 12)) >> 13)) + 3998c2ecf20Sopenharmony_ci (((s16)tregs->y1) << 3); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci return (s32)val; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic s32 bmc150_magn_compensate_z(struct bmc150_magn_trim_regs *tregs, s16 z, 4058c2ecf20Sopenharmony_ci u16 rhall) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci s32 val; 4088c2ecf20Sopenharmony_ci u16 xyz1 = le16_to_cpu(tregs->xyz1); 4098c2ecf20Sopenharmony_ci u16 z1 = le16_to_cpu(tregs->z1); 4108c2ecf20Sopenharmony_ci s16 z2 = le16_to_cpu(tregs->z2); 4118c2ecf20Sopenharmony_ci s16 z3 = le16_to_cpu(tregs->z3); 4128c2ecf20Sopenharmony_ci s16 z4 = le16_to_cpu(tregs->z4); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (z == BMC150_MAGN_Z_OVERFLOW_VAL) 4158c2ecf20Sopenharmony_ci return S32_MIN; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci val = (((((s32)(z - z4)) << 15) - ((((s32)z3) * ((s32)(((s16)rhall) - 4188c2ecf20Sopenharmony_ci ((s16)xyz1)))) >> 2)) / (z2 + ((s16)(((((s32)z1) * 4198c2ecf20Sopenharmony_ci ((((s16)rhall) << 1))) + (1 << 15)) >> 16)))); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci return val; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic int bmc150_magn_read_xyz(struct bmc150_magn_data *data, s32 *buffer) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci int ret; 4278c2ecf20Sopenharmony_ci __le16 values[AXIS_XYZR_MAX]; 4288c2ecf20Sopenharmony_ci s16 raw_x, raw_y, raw_z; 4298c2ecf20Sopenharmony_ci u16 rhall; 4308c2ecf20Sopenharmony_ci struct bmc150_magn_trim_regs tregs; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci ret = regmap_bulk_read(data->regmap, BMC150_MAGN_REG_X_L, 4338c2ecf20Sopenharmony_ci values, sizeof(values)); 4348c2ecf20Sopenharmony_ci if (ret < 0) 4358c2ecf20Sopenharmony_ci return ret; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci raw_x = (s16)le16_to_cpu(values[AXIS_X]) >> BMC150_MAGN_SHIFT_XY_L; 4388c2ecf20Sopenharmony_ci raw_y = (s16)le16_to_cpu(values[AXIS_Y]) >> BMC150_MAGN_SHIFT_XY_L; 4398c2ecf20Sopenharmony_ci raw_z = (s16)le16_to_cpu(values[AXIS_Z]) >> BMC150_MAGN_SHIFT_Z_L; 4408c2ecf20Sopenharmony_ci rhall = le16_to_cpu(values[RHALL]) >> BMC150_MAGN_SHIFT_RHALL_L; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci ret = regmap_bulk_read(data->regmap, BMC150_MAGN_REG_TRIM_START, 4438c2ecf20Sopenharmony_ci &tregs, sizeof(tregs)); 4448c2ecf20Sopenharmony_ci if (ret < 0) 4458c2ecf20Sopenharmony_ci return ret; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci buffer[AXIS_X] = bmc150_magn_compensate_x(&tregs, raw_x, rhall); 4488c2ecf20Sopenharmony_ci buffer[AXIS_Y] = bmc150_magn_compensate_y(&tregs, raw_y, rhall); 4498c2ecf20Sopenharmony_ci buffer[AXIS_Z] = bmc150_magn_compensate_z(&tregs, raw_z, rhall); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci return 0; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic int bmc150_magn_read_raw(struct iio_dev *indio_dev, 4558c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 4568c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci struct bmc150_magn_data *data = iio_priv(indio_dev); 4598c2ecf20Sopenharmony_ci int ret, tmp; 4608c2ecf20Sopenharmony_ci s32 values[AXIS_XYZ_MAX]; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci switch (mask) { 4638c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 4648c2ecf20Sopenharmony_ci if (iio_buffer_enabled(indio_dev)) 4658c2ecf20Sopenharmony_ci return -EBUSY; 4668c2ecf20Sopenharmony_ci mutex_lock(&data->mutex); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci ret = bmc150_magn_set_power_state(data, true); 4698c2ecf20Sopenharmony_ci if (ret < 0) { 4708c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 4718c2ecf20Sopenharmony_ci return ret; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci ret = bmc150_magn_read_xyz(data, values); 4758c2ecf20Sopenharmony_ci if (ret < 0) { 4768c2ecf20Sopenharmony_ci bmc150_magn_set_power_state(data, false); 4778c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 4788c2ecf20Sopenharmony_ci return ret; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci *val = values[chan->scan_index]; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci ret = bmc150_magn_set_power_state(data, false); 4838c2ecf20Sopenharmony_ci if (ret < 0) { 4848c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 4858c2ecf20Sopenharmony_ci return ret; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 4898c2ecf20Sopenharmony_ci return IIO_VAL_INT; 4908c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 4918c2ecf20Sopenharmony_ci /* 4928c2ecf20Sopenharmony_ci * The API/driver performs an off-chip temperature 4938c2ecf20Sopenharmony_ci * compensation and outputs x/y/z magnetic field data in 4948c2ecf20Sopenharmony_ci * 16 LSB/uT to the upper application layer. 4958c2ecf20Sopenharmony_ci */ 4968c2ecf20Sopenharmony_ci *val = 0; 4978c2ecf20Sopenharmony_ci *val2 = 625; 4988c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 4998c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: 5008c2ecf20Sopenharmony_ci ret = bmc150_magn_get_odr(data, val); 5018c2ecf20Sopenharmony_ci if (ret < 0) 5028c2ecf20Sopenharmony_ci return ret; 5038c2ecf20Sopenharmony_ci return IIO_VAL_INT; 5048c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_OVERSAMPLING_RATIO: 5058c2ecf20Sopenharmony_ci switch (chan->channel2) { 5068c2ecf20Sopenharmony_ci case IIO_MOD_X: 5078c2ecf20Sopenharmony_ci case IIO_MOD_Y: 5088c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, BMC150_MAGN_REG_REP_XY, 5098c2ecf20Sopenharmony_ci &tmp); 5108c2ecf20Sopenharmony_ci if (ret < 0) 5118c2ecf20Sopenharmony_ci return ret; 5128c2ecf20Sopenharmony_ci *val = BMC150_MAGN_REGVAL_TO_REPXY(tmp); 5138c2ecf20Sopenharmony_ci return IIO_VAL_INT; 5148c2ecf20Sopenharmony_ci case IIO_MOD_Z: 5158c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, BMC150_MAGN_REG_REP_Z, 5168c2ecf20Sopenharmony_ci &tmp); 5178c2ecf20Sopenharmony_ci if (ret < 0) 5188c2ecf20Sopenharmony_ci return ret; 5198c2ecf20Sopenharmony_ci *val = BMC150_MAGN_REGVAL_TO_REPZ(tmp); 5208c2ecf20Sopenharmony_ci return IIO_VAL_INT; 5218c2ecf20Sopenharmony_ci default: 5228c2ecf20Sopenharmony_ci return -EINVAL; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci default: 5258c2ecf20Sopenharmony_ci return -EINVAL; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic int bmc150_magn_write_raw(struct iio_dev *indio_dev, 5308c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 5318c2ecf20Sopenharmony_ci int val, int val2, long mask) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci struct bmc150_magn_data *data = iio_priv(indio_dev); 5348c2ecf20Sopenharmony_ci int ret; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci switch (mask) { 5378c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: 5388c2ecf20Sopenharmony_ci if (val > data->max_odr) 5398c2ecf20Sopenharmony_ci return -EINVAL; 5408c2ecf20Sopenharmony_ci mutex_lock(&data->mutex); 5418c2ecf20Sopenharmony_ci ret = bmc150_magn_set_odr(data, val); 5428c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 5438c2ecf20Sopenharmony_ci return ret; 5448c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_OVERSAMPLING_RATIO: 5458c2ecf20Sopenharmony_ci switch (chan->channel2) { 5468c2ecf20Sopenharmony_ci case IIO_MOD_X: 5478c2ecf20Sopenharmony_ci case IIO_MOD_Y: 5488c2ecf20Sopenharmony_ci if (val < 1 || val > 511) 5498c2ecf20Sopenharmony_ci return -EINVAL; 5508c2ecf20Sopenharmony_ci mutex_lock(&data->mutex); 5518c2ecf20Sopenharmony_ci ret = bmc150_magn_set_max_odr(data, val, 0, 0); 5528c2ecf20Sopenharmony_ci if (ret < 0) { 5538c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 5548c2ecf20Sopenharmony_ci return ret; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, 5578c2ecf20Sopenharmony_ci BMC150_MAGN_REG_REP_XY, 5588c2ecf20Sopenharmony_ci BMC150_MAGN_REG_REP_DATAMASK, 5598c2ecf20Sopenharmony_ci BMC150_MAGN_REPXY_TO_REGVAL 5608c2ecf20Sopenharmony_ci (val)); 5618c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 5628c2ecf20Sopenharmony_ci return ret; 5638c2ecf20Sopenharmony_ci case IIO_MOD_Z: 5648c2ecf20Sopenharmony_ci if (val < 1 || val > 256) 5658c2ecf20Sopenharmony_ci return -EINVAL; 5668c2ecf20Sopenharmony_ci mutex_lock(&data->mutex); 5678c2ecf20Sopenharmony_ci ret = bmc150_magn_set_max_odr(data, 0, val, 0); 5688c2ecf20Sopenharmony_ci if (ret < 0) { 5698c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 5708c2ecf20Sopenharmony_ci return ret; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, 5738c2ecf20Sopenharmony_ci BMC150_MAGN_REG_REP_Z, 5748c2ecf20Sopenharmony_ci BMC150_MAGN_REG_REP_DATAMASK, 5758c2ecf20Sopenharmony_ci BMC150_MAGN_REPZ_TO_REGVAL 5768c2ecf20Sopenharmony_ci (val)); 5778c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 5788c2ecf20Sopenharmony_ci return ret; 5798c2ecf20Sopenharmony_ci default: 5808c2ecf20Sopenharmony_ci return -EINVAL; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci default: 5838c2ecf20Sopenharmony_ci return -EINVAL; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistatic ssize_t bmc150_magn_show_samp_freq_avail(struct device *dev, 5888c2ecf20Sopenharmony_ci struct device_attribute *attr, 5898c2ecf20Sopenharmony_ci char *buf) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 5928c2ecf20Sopenharmony_ci struct bmc150_magn_data *data = iio_priv(indio_dev); 5938c2ecf20Sopenharmony_ci size_t len = 0; 5948c2ecf20Sopenharmony_ci u8 i; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(bmc150_magn_samp_freq_table); i++) { 5978c2ecf20Sopenharmony_ci if (bmc150_magn_samp_freq_table[i].freq > data->max_odr) 5988c2ecf20Sopenharmony_ci break; 5998c2ecf20Sopenharmony_ci len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", 6008c2ecf20Sopenharmony_ci bmc150_magn_samp_freq_table[i].freq); 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci /* replace last space with a newline */ 6038c2ecf20Sopenharmony_ci buf[len - 1] = '\n'; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci return len; 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic const struct iio_mount_matrix * 6098c2ecf20Sopenharmony_cibmc150_magn_get_mount_matrix(const struct iio_dev *indio_dev, 6108c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct bmc150_magn_data *data = iio_priv(indio_dev); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci return &data->orientation; 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cistatic const struct iio_chan_spec_ext_info bmc150_magn_ext_info[] = { 6188c2ecf20Sopenharmony_ci IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, bmc150_magn_get_mount_matrix), 6198c2ecf20Sopenharmony_ci { } 6208c2ecf20Sopenharmony_ci}; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic IIO_DEV_ATTR_SAMP_FREQ_AVAIL(bmc150_magn_show_samp_freq_avail); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_cistatic struct attribute *bmc150_magn_attributes[] = { 6258c2ecf20Sopenharmony_ci &iio_dev_attr_sampling_frequency_available.dev_attr.attr, 6268c2ecf20Sopenharmony_ci NULL, 6278c2ecf20Sopenharmony_ci}; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_cistatic const struct attribute_group bmc150_magn_attrs_group = { 6308c2ecf20Sopenharmony_ci .attrs = bmc150_magn_attributes, 6318c2ecf20Sopenharmony_ci}; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci#define BMC150_MAGN_CHANNEL(_axis) { \ 6348c2ecf20Sopenharmony_ci .type = IIO_MAGN, \ 6358c2ecf20Sopenharmony_ci .modified = 1, \ 6368c2ecf20Sopenharmony_ci .channel2 = IIO_MOD_##_axis, \ 6378c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ 6388c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ 6398c2ecf20Sopenharmony_ci .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ 6408c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), \ 6418c2ecf20Sopenharmony_ci .scan_index = AXIS_##_axis, \ 6428c2ecf20Sopenharmony_ci .scan_type = { \ 6438c2ecf20Sopenharmony_ci .sign = 's', \ 6448c2ecf20Sopenharmony_ci .realbits = 32, \ 6458c2ecf20Sopenharmony_ci .storagebits = 32, \ 6468c2ecf20Sopenharmony_ci .endianness = IIO_LE \ 6478c2ecf20Sopenharmony_ci }, \ 6488c2ecf20Sopenharmony_ci .ext_info = bmc150_magn_ext_info, \ 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic const struct iio_chan_spec bmc150_magn_channels[] = { 6528c2ecf20Sopenharmony_ci BMC150_MAGN_CHANNEL(X), 6538c2ecf20Sopenharmony_ci BMC150_MAGN_CHANNEL(Y), 6548c2ecf20Sopenharmony_ci BMC150_MAGN_CHANNEL(Z), 6558c2ecf20Sopenharmony_ci IIO_CHAN_SOFT_TIMESTAMP(3), 6568c2ecf20Sopenharmony_ci}; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic const struct iio_info bmc150_magn_info = { 6598c2ecf20Sopenharmony_ci .attrs = &bmc150_magn_attrs_group, 6608c2ecf20Sopenharmony_ci .read_raw = bmc150_magn_read_raw, 6618c2ecf20Sopenharmony_ci .write_raw = bmc150_magn_write_raw, 6628c2ecf20Sopenharmony_ci}; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic const unsigned long bmc150_magn_scan_masks[] = { 6658c2ecf20Sopenharmony_ci BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z), 6668c2ecf20Sopenharmony_ci 0}; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic irqreturn_t bmc150_magn_trigger_handler(int irq, void *p) 6698c2ecf20Sopenharmony_ci{ 6708c2ecf20Sopenharmony_ci struct iio_poll_func *pf = p; 6718c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = pf->indio_dev; 6728c2ecf20Sopenharmony_ci struct bmc150_magn_data *data = iio_priv(indio_dev); 6738c2ecf20Sopenharmony_ci int ret; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci mutex_lock(&data->mutex); 6768c2ecf20Sopenharmony_ci ret = bmc150_magn_read_xyz(data, data->scan.chans); 6778c2ecf20Sopenharmony_ci if (ret < 0) 6788c2ecf20Sopenharmony_ci goto err; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, 6818c2ecf20Sopenharmony_ci pf->timestamp); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_cierr: 6848c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 6858c2ecf20Sopenharmony_ci iio_trigger_notify_done(indio_dev->trig); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_cistatic int bmc150_magn_init(struct bmc150_magn_data *data) 6918c2ecf20Sopenharmony_ci{ 6928c2ecf20Sopenharmony_ci int ret, chip_id; 6938c2ecf20Sopenharmony_ci struct bmc150_magn_preset preset; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, 6968c2ecf20Sopenharmony_ci false); 6978c2ecf20Sopenharmony_ci if (ret < 0) { 6988c2ecf20Sopenharmony_ci dev_err(data->dev, 6998c2ecf20Sopenharmony_ci "Failed to bring up device from suspend mode\n"); 7008c2ecf20Sopenharmony_ci return ret; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, BMC150_MAGN_REG_CHIP_ID, &chip_id); 7048c2ecf20Sopenharmony_ci if (ret < 0) { 7058c2ecf20Sopenharmony_ci dev_err(data->dev, "Failed reading chip id\n"); 7068c2ecf20Sopenharmony_ci goto err_poweroff; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci if (chip_id != BMC150_MAGN_CHIP_ID_VAL) { 7098c2ecf20Sopenharmony_ci dev_err(data->dev, "Invalid chip id 0x%x\n", chip_id); 7108c2ecf20Sopenharmony_ci ret = -ENODEV; 7118c2ecf20Sopenharmony_ci goto err_poweroff; 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci dev_dbg(data->dev, "Chip id %x\n", chip_id); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci preset = bmc150_magn_presets_table[BMC150_MAGN_DEFAULT_PRESET]; 7168c2ecf20Sopenharmony_ci ret = bmc150_magn_set_odr(data, preset.odr); 7178c2ecf20Sopenharmony_ci if (ret < 0) { 7188c2ecf20Sopenharmony_ci dev_err(data->dev, "Failed to set ODR to %d\n", 7198c2ecf20Sopenharmony_ci preset.odr); 7208c2ecf20Sopenharmony_ci goto err_poweroff; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci ret = regmap_write(data->regmap, BMC150_MAGN_REG_REP_XY, 7248c2ecf20Sopenharmony_ci BMC150_MAGN_REPXY_TO_REGVAL(preset.rep_xy)); 7258c2ecf20Sopenharmony_ci if (ret < 0) { 7268c2ecf20Sopenharmony_ci dev_err(data->dev, "Failed to set REP XY to %d\n", 7278c2ecf20Sopenharmony_ci preset.rep_xy); 7288c2ecf20Sopenharmony_ci goto err_poweroff; 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci ret = regmap_write(data->regmap, BMC150_MAGN_REG_REP_Z, 7328c2ecf20Sopenharmony_ci BMC150_MAGN_REPZ_TO_REGVAL(preset.rep_z)); 7338c2ecf20Sopenharmony_ci if (ret < 0) { 7348c2ecf20Sopenharmony_ci dev_err(data->dev, "Failed to set REP Z to %d\n", 7358c2ecf20Sopenharmony_ci preset.rep_z); 7368c2ecf20Sopenharmony_ci goto err_poweroff; 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci ret = bmc150_magn_set_max_odr(data, preset.rep_xy, preset.rep_z, 7408c2ecf20Sopenharmony_ci preset.odr); 7418c2ecf20Sopenharmony_ci if (ret < 0) 7428c2ecf20Sopenharmony_ci goto err_poweroff; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_NORMAL, 7458c2ecf20Sopenharmony_ci true); 7468c2ecf20Sopenharmony_ci if (ret < 0) { 7478c2ecf20Sopenharmony_ci dev_err(data->dev, "Failed to power on device\n"); 7488c2ecf20Sopenharmony_ci goto err_poweroff; 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci return 0; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_cierr_poweroff: 7548c2ecf20Sopenharmony_ci bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true); 7558c2ecf20Sopenharmony_ci return ret; 7568c2ecf20Sopenharmony_ci} 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_cistatic int bmc150_magn_reset_intr(struct bmc150_magn_data *data) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci int tmp; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci /* 7638c2ecf20Sopenharmony_ci * Data Ready (DRDY) is always cleared after 7648c2ecf20Sopenharmony_ci * readout of data registers ends. 7658c2ecf20Sopenharmony_ci */ 7668c2ecf20Sopenharmony_ci return regmap_read(data->regmap, BMC150_MAGN_REG_X_L, &tmp); 7678c2ecf20Sopenharmony_ci} 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cistatic int bmc150_magn_trig_try_reen(struct iio_trigger *trig) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); 7728c2ecf20Sopenharmony_ci struct bmc150_magn_data *data = iio_priv(indio_dev); 7738c2ecf20Sopenharmony_ci int ret; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci if (!data->dready_trigger_on) 7768c2ecf20Sopenharmony_ci return 0; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci mutex_lock(&data->mutex); 7798c2ecf20Sopenharmony_ci ret = bmc150_magn_reset_intr(data); 7808c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci return ret; 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_cistatic int bmc150_magn_data_rdy_trigger_set_state(struct iio_trigger *trig, 7868c2ecf20Sopenharmony_ci bool state) 7878c2ecf20Sopenharmony_ci{ 7888c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); 7898c2ecf20Sopenharmony_ci struct bmc150_magn_data *data = iio_priv(indio_dev); 7908c2ecf20Sopenharmony_ci int ret = 0; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci mutex_lock(&data->mutex); 7938c2ecf20Sopenharmony_ci if (state == data->dready_trigger_on) 7948c2ecf20Sopenharmony_ci goto err_unlock; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, BMC150_MAGN_REG_INT_DRDY, 7978c2ecf20Sopenharmony_ci BMC150_MAGN_MASK_DRDY_EN, 7988c2ecf20Sopenharmony_ci state << BMC150_MAGN_SHIFT_DRDY_EN); 7998c2ecf20Sopenharmony_ci if (ret < 0) 8008c2ecf20Sopenharmony_ci goto err_unlock; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci data->dready_trigger_on = state; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci if (state) { 8058c2ecf20Sopenharmony_ci ret = bmc150_magn_reset_intr(data); 8068c2ecf20Sopenharmony_ci if (ret < 0) 8078c2ecf20Sopenharmony_ci goto err_unlock; 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci return 0; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_cierr_unlock: 8148c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 8158c2ecf20Sopenharmony_ci return ret; 8168c2ecf20Sopenharmony_ci} 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_cistatic const struct iio_trigger_ops bmc150_magn_trigger_ops = { 8198c2ecf20Sopenharmony_ci .set_trigger_state = bmc150_magn_data_rdy_trigger_set_state, 8208c2ecf20Sopenharmony_ci .try_reenable = bmc150_magn_trig_try_reen, 8218c2ecf20Sopenharmony_ci}; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic int bmc150_magn_buffer_preenable(struct iio_dev *indio_dev) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci struct bmc150_magn_data *data = iio_priv(indio_dev); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci return bmc150_magn_set_power_state(data, true); 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_cistatic int bmc150_magn_buffer_postdisable(struct iio_dev *indio_dev) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci struct bmc150_magn_data *data = iio_priv(indio_dev); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci return bmc150_magn_set_power_state(data, false); 8358c2ecf20Sopenharmony_ci} 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_cistatic const struct iio_buffer_setup_ops bmc150_magn_buffer_setup_ops = { 8388c2ecf20Sopenharmony_ci .preenable = bmc150_magn_buffer_preenable, 8398c2ecf20Sopenharmony_ci .postdisable = bmc150_magn_buffer_postdisable, 8408c2ecf20Sopenharmony_ci}; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_cistatic const char *bmc150_magn_match_acpi_device(struct device *dev) 8438c2ecf20Sopenharmony_ci{ 8448c2ecf20Sopenharmony_ci const struct acpi_device_id *id; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci id = acpi_match_device(dev->driver->acpi_match_table, dev); 8478c2ecf20Sopenharmony_ci if (!id) 8488c2ecf20Sopenharmony_ci return NULL; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci return dev_name(dev); 8518c2ecf20Sopenharmony_ci} 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ciint bmc150_magn_probe(struct device *dev, struct regmap *regmap, 8548c2ecf20Sopenharmony_ci int irq, const char *name) 8558c2ecf20Sopenharmony_ci{ 8568c2ecf20Sopenharmony_ci struct bmc150_magn_data *data; 8578c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 8588c2ecf20Sopenharmony_ci int ret; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); 8618c2ecf20Sopenharmony_ci if (!indio_dev) 8628c2ecf20Sopenharmony_ci return -ENOMEM; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 8658c2ecf20Sopenharmony_ci dev_set_drvdata(dev, indio_dev); 8668c2ecf20Sopenharmony_ci data->regmap = regmap; 8678c2ecf20Sopenharmony_ci data->irq = irq; 8688c2ecf20Sopenharmony_ci data->dev = dev; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci ret = iio_read_mount_matrix(dev, "mount-matrix", 8718c2ecf20Sopenharmony_ci &data->orientation); 8728c2ecf20Sopenharmony_ci if (ret) 8738c2ecf20Sopenharmony_ci return ret; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci if (!name && ACPI_HANDLE(dev)) 8768c2ecf20Sopenharmony_ci name = bmc150_magn_match_acpi_device(dev); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci mutex_init(&data->mutex); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci ret = bmc150_magn_init(data); 8818c2ecf20Sopenharmony_ci if (ret < 0) 8828c2ecf20Sopenharmony_ci return ret; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci indio_dev->channels = bmc150_magn_channels; 8858c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(bmc150_magn_channels); 8868c2ecf20Sopenharmony_ci indio_dev->available_scan_masks = bmc150_magn_scan_masks; 8878c2ecf20Sopenharmony_ci indio_dev->name = name; 8888c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 8898c2ecf20Sopenharmony_ci indio_dev->info = &bmc150_magn_info; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci if (irq > 0) { 8928c2ecf20Sopenharmony_ci data->dready_trig = devm_iio_trigger_alloc(dev, 8938c2ecf20Sopenharmony_ci "%s-dev%d", 8948c2ecf20Sopenharmony_ci indio_dev->name, 8958c2ecf20Sopenharmony_ci indio_dev->id); 8968c2ecf20Sopenharmony_ci if (!data->dready_trig) { 8978c2ecf20Sopenharmony_ci ret = -ENOMEM; 8988c2ecf20Sopenharmony_ci dev_err(dev, "iio trigger alloc failed\n"); 8998c2ecf20Sopenharmony_ci goto err_poweroff; 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci data->dready_trig->dev.parent = dev; 9038c2ecf20Sopenharmony_ci data->dready_trig->ops = &bmc150_magn_trigger_ops; 9048c2ecf20Sopenharmony_ci iio_trigger_set_drvdata(data->dready_trig, indio_dev); 9058c2ecf20Sopenharmony_ci ret = iio_trigger_register(data->dready_trig); 9068c2ecf20Sopenharmony_ci if (ret) { 9078c2ecf20Sopenharmony_ci dev_err(dev, "iio trigger register failed\n"); 9088c2ecf20Sopenharmony_ci goto err_poweroff; 9098c2ecf20Sopenharmony_ci } 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci ret = request_threaded_irq(irq, 9128c2ecf20Sopenharmony_ci iio_trigger_generic_data_rdy_poll, 9138c2ecf20Sopenharmony_ci NULL, 9148c2ecf20Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_ONESHOT, 9158c2ecf20Sopenharmony_ci BMC150_MAGN_IRQ_NAME, 9168c2ecf20Sopenharmony_ci data->dready_trig); 9178c2ecf20Sopenharmony_ci if (ret < 0) { 9188c2ecf20Sopenharmony_ci dev_err(dev, "request irq %d failed\n", irq); 9198c2ecf20Sopenharmony_ci goto err_trigger_unregister; 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci ret = iio_triggered_buffer_setup(indio_dev, 9248c2ecf20Sopenharmony_ci iio_pollfunc_store_time, 9258c2ecf20Sopenharmony_ci bmc150_magn_trigger_handler, 9268c2ecf20Sopenharmony_ci &bmc150_magn_buffer_setup_ops); 9278c2ecf20Sopenharmony_ci if (ret < 0) { 9288c2ecf20Sopenharmony_ci dev_err(dev, "iio triggered buffer setup failed\n"); 9298c2ecf20Sopenharmony_ci goto err_free_irq; 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci ret = pm_runtime_set_active(dev); 9338c2ecf20Sopenharmony_ci if (ret) 9348c2ecf20Sopenharmony_ci goto err_buffer_cleanup; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 9378c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(dev, 9388c2ecf20Sopenharmony_ci BMC150_MAGN_AUTO_SUSPEND_DELAY_MS); 9398c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(dev); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci ret = iio_device_register(indio_dev); 9428c2ecf20Sopenharmony_ci if (ret < 0) { 9438c2ecf20Sopenharmony_ci dev_err(dev, "unable to register iio device\n"); 9448c2ecf20Sopenharmony_ci goto err_pm_cleanup; 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci dev_dbg(dev, "Registered device %s\n", name); 9488c2ecf20Sopenharmony_ci return 0; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_cierr_pm_cleanup: 9518c2ecf20Sopenharmony_ci pm_runtime_dont_use_autosuspend(dev); 9528c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 9538c2ecf20Sopenharmony_cierr_buffer_cleanup: 9548c2ecf20Sopenharmony_ci iio_triggered_buffer_cleanup(indio_dev); 9558c2ecf20Sopenharmony_cierr_free_irq: 9568c2ecf20Sopenharmony_ci if (irq > 0) 9578c2ecf20Sopenharmony_ci free_irq(irq, data->dready_trig); 9588c2ecf20Sopenharmony_cierr_trigger_unregister: 9598c2ecf20Sopenharmony_ci if (data->dready_trig) 9608c2ecf20Sopenharmony_ci iio_trigger_unregister(data->dready_trig); 9618c2ecf20Sopenharmony_cierr_poweroff: 9628c2ecf20Sopenharmony_ci bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true); 9638c2ecf20Sopenharmony_ci return ret; 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bmc150_magn_probe); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ciint bmc150_magn_remove(struct device *dev) 9688c2ecf20Sopenharmony_ci{ 9698c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_get_drvdata(dev); 9708c2ecf20Sopenharmony_ci struct bmc150_magn_data *data = iio_priv(indio_dev); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci iio_device_unregister(indio_dev); 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 9758c2ecf20Sopenharmony_ci pm_runtime_set_suspended(dev); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci iio_triggered_buffer_cleanup(indio_dev); 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci if (data->irq > 0) 9808c2ecf20Sopenharmony_ci free_irq(data->irq, data->dready_trig); 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci if (data->dready_trig) 9838c2ecf20Sopenharmony_ci iio_trigger_unregister(data->dready_trig); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci mutex_lock(&data->mutex); 9868c2ecf20Sopenharmony_ci bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true); 9878c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci return 0; 9908c2ecf20Sopenharmony_ci} 9918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bmc150_magn_remove); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 9948c2ecf20Sopenharmony_cistatic int bmc150_magn_runtime_suspend(struct device *dev) 9958c2ecf20Sopenharmony_ci{ 9968c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_get_drvdata(dev); 9978c2ecf20Sopenharmony_ci struct bmc150_magn_data *data = iio_priv(indio_dev); 9988c2ecf20Sopenharmony_ci int ret; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci mutex_lock(&data->mutex); 10018c2ecf20Sopenharmony_ci ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SLEEP, 10028c2ecf20Sopenharmony_ci true); 10038c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 10048c2ecf20Sopenharmony_ci if (ret < 0) { 10058c2ecf20Sopenharmony_ci dev_err(dev, "powering off device failed\n"); 10068c2ecf20Sopenharmony_ci return ret; 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci return 0; 10098c2ecf20Sopenharmony_ci} 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci/* 10128c2ecf20Sopenharmony_ci * Should be called with data->mutex held. 10138c2ecf20Sopenharmony_ci */ 10148c2ecf20Sopenharmony_cistatic int bmc150_magn_runtime_resume(struct device *dev) 10158c2ecf20Sopenharmony_ci{ 10168c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_get_drvdata(dev); 10178c2ecf20Sopenharmony_ci struct bmc150_magn_data *data = iio_priv(indio_dev); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci return bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_NORMAL, 10208c2ecf20Sopenharmony_ci true); 10218c2ecf20Sopenharmony_ci} 10228c2ecf20Sopenharmony_ci#endif 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 10258c2ecf20Sopenharmony_cistatic int bmc150_magn_suspend(struct device *dev) 10268c2ecf20Sopenharmony_ci{ 10278c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_get_drvdata(dev); 10288c2ecf20Sopenharmony_ci struct bmc150_magn_data *data = iio_priv(indio_dev); 10298c2ecf20Sopenharmony_ci int ret; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci mutex_lock(&data->mutex); 10328c2ecf20Sopenharmony_ci ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SLEEP, 10338c2ecf20Sopenharmony_ci true); 10348c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci return ret; 10378c2ecf20Sopenharmony_ci} 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_cistatic int bmc150_magn_resume(struct device *dev) 10408c2ecf20Sopenharmony_ci{ 10418c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_get_drvdata(dev); 10428c2ecf20Sopenharmony_ci struct bmc150_magn_data *data = iio_priv(indio_dev); 10438c2ecf20Sopenharmony_ci int ret; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci mutex_lock(&data->mutex); 10468c2ecf20Sopenharmony_ci ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_NORMAL, 10478c2ecf20Sopenharmony_ci true); 10488c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci return ret; 10518c2ecf20Sopenharmony_ci} 10528c2ecf20Sopenharmony_ci#endif 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ciconst struct dev_pm_ops bmc150_magn_pm_ops = { 10558c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(bmc150_magn_suspend, bmc150_magn_resume) 10568c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(bmc150_magn_runtime_suspend, 10578c2ecf20Sopenharmony_ci bmc150_magn_runtime_resume, NULL) 10588c2ecf20Sopenharmony_ci}; 10598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bmc150_magn_pm_ops); 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ciMODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>"); 10628c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 10638c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("BMC150 magnetometer core driver"); 1064