162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * mag3110.c - Support for Freescale MAG3110 magnetometer sensor 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2013 Peter Meerwald <pmeerw@pmeerw.net> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * (7-bit I2C slave address 0x0e) 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * TODO: irq, user offset, oversampling, continuous mode 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/i2c.h> 1462306a36Sopenharmony_ci#include <linux/iio/iio.h> 1562306a36Sopenharmony_ci#include <linux/iio/sysfs.h> 1662306a36Sopenharmony_ci#include <linux/iio/trigger_consumer.h> 1762306a36Sopenharmony_ci#include <linux/iio/buffer.h> 1862306a36Sopenharmony_ci#include <linux/iio/triggered_buffer.h> 1962306a36Sopenharmony_ci#include <linux/delay.h> 2062306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define MAG3110_STATUS 0x00 2362306a36Sopenharmony_ci#define MAG3110_OUT_X 0x01 /* MSB first */ 2462306a36Sopenharmony_ci#define MAG3110_OUT_Y 0x03 2562306a36Sopenharmony_ci#define MAG3110_OUT_Z 0x05 2662306a36Sopenharmony_ci#define MAG3110_WHO_AM_I 0x07 2762306a36Sopenharmony_ci#define MAG3110_SYSMOD 0x08 2862306a36Sopenharmony_ci#define MAG3110_OFF_X 0x09 /* MSB first */ 2962306a36Sopenharmony_ci#define MAG3110_OFF_Y 0x0b 3062306a36Sopenharmony_ci#define MAG3110_OFF_Z 0x0d 3162306a36Sopenharmony_ci#define MAG3110_DIE_TEMP 0x0f 3262306a36Sopenharmony_ci#define MAG3110_CTRL_REG1 0x10 3362306a36Sopenharmony_ci#define MAG3110_CTRL_REG2 0x11 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define MAG3110_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0)) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define MAG3110_CTRL_DR_MASK (BIT(7) | BIT(6) | BIT(5)) 3862306a36Sopenharmony_ci#define MAG3110_CTRL_DR_SHIFT 5 3962306a36Sopenharmony_ci#define MAG3110_CTRL_DR_DEFAULT 0 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define MAG3110_SYSMOD_MODE_MASK GENMASK(1, 0) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define MAG3110_CTRL_TM BIT(1) /* trigger single measurement */ 4462306a36Sopenharmony_ci#define MAG3110_CTRL_AC BIT(0) /* continuous measurements */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define MAG3110_CTRL_AUTO_MRST_EN BIT(7) /* magnetic auto-reset */ 4762306a36Sopenharmony_ci#define MAG3110_CTRL_RAW BIT(5) /* measurements not user-offset corrected */ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define MAG3110_DEVICE_ID 0xc4 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* Each client has this additional data */ 5262306a36Sopenharmony_cistruct mag3110_data { 5362306a36Sopenharmony_ci struct i2c_client *client; 5462306a36Sopenharmony_ci struct mutex lock; 5562306a36Sopenharmony_ci u8 ctrl_reg1; 5662306a36Sopenharmony_ci int sleep_val; 5762306a36Sopenharmony_ci struct regulator *vdd_reg; 5862306a36Sopenharmony_ci struct regulator *vddio_reg; 5962306a36Sopenharmony_ci /* Ensure natural alignment of timestamp */ 6062306a36Sopenharmony_ci struct { 6162306a36Sopenharmony_ci __be16 channels[3]; 6262306a36Sopenharmony_ci u8 temperature; 6362306a36Sopenharmony_ci s64 ts __aligned(8); 6462306a36Sopenharmony_ci } scan; 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int mag3110_request(struct mag3110_data *data) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci int ret, tries = 150; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if ((data->ctrl_reg1 & MAG3110_CTRL_AC) == 0) { 7262306a36Sopenharmony_ci /* trigger measurement */ 7362306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1, 7462306a36Sopenharmony_ci data->ctrl_reg1 | MAG3110_CTRL_TM); 7562306a36Sopenharmony_ci if (ret < 0) 7662306a36Sopenharmony_ci return ret; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci while (tries-- > 0) { 8062306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, MAG3110_STATUS); 8162306a36Sopenharmony_ci if (ret < 0) 8262306a36Sopenharmony_ci return ret; 8362306a36Sopenharmony_ci /* wait for data ready */ 8462306a36Sopenharmony_ci if ((ret & MAG3110_STATUS_DRDY) == MAG3110_STATUS_DRDY) 8562306a36Sopenharmony_ci break; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (data->sleep_val <= 20) 8862306a36Sopenharmony_ci usleep_range(data->sleep_val * 250, data->sleep_val * 500); 8962306a36Sopenharmony_ci else 9062306a36Sopenharmony_ci msleep(20); 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (tries < 0) { 9462306a36Sopenharmony_ci dev_err(&data->client->dev, "data not ready\n"); 9562306a36Sopenharmony_ci return -EIO; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic int mag3110_read(struct mag3110_data *data, __be16 buf[3]) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci int ret; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci mutex_lock(&data->lock); 10662306a36Sopenharmony_ci ret = mag3110_request(data); 10762306a36Sopenharmony_ci if (ret < 0) { 10862306a36Sopenharmony_ci mutex_unlock(&data->lock); 10962306a36Sopenharmony_ci return ret; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci ret = i2c_smbus_read_i2c_block_data(data->client, 11262306a36Sopenharmony_ci MAG3110_OUT_X, 3 * sizeof(__be16), (u8 *) buf); 11362306a36Sopenharmony_ci mutex_unlock(&data->lock); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return ret; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic ssize_t mag3110_show_int_plus_micros(char *buf, 11962306a36Sopenharmony_ci const int (*vals)[2], int n) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci size_t len = 0; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci while (n-- > 0) 12462306a36Sopenharmony_ci len += scnprintf(buf + len, PAGE_SIZE - len, 12562306a36Sopenharmony_ci "%d.%06d ", vals[n][0], vals[n][1]); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* replace trailing space by newline */ 12862306a36Sopenharmony_ci buf[len - 1] = '\n'; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return len; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int mag3110_get_int_plus_micros_index(const int (*vals)[2], int n, 13462306a36Sopenharmony_ci int val, int val2) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci while (n-- > 0) 13762306a36Sopenharmony_ci if (val == vals[n][0] && val2 == vals[n][1]) 13862306a36Sopenharmony_ci return n; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return -EINVAL; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic const int mag3110_samp_freq[8][2] = { 14462306a36Sopenharmony_ci {80, 0}, {40, 0}, {20, 0}, {10, 0}, {5, 0}, {2, 500000}, 14562306a36Sopenharmony_ci {1, 250000}, {0, 625000} 14662306a36Sopenharmony_ci}; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic ssize_t mag3110_show_samp_freq_avail(struct device *dev, 14962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci return mag3110_show_int_plus_micros(buf, mag3110_samp_freq, 8); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mag3110_show_samp_freq_avail); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int mag3110_get_samp_freq_index(struct mag3110_data *data, 15762306a36Sopenharmony_ci int val, int val2) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci return mag3110_get_int_plus_micros_index(mag3110_samp_freq, 8, val, 16062306a36Sopenharmony_ci val2); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int mag3110_calculate_sleep(struct mag3110_data *data) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci int ret, i = data->ctrl_reg1 >> MAG3110_CTRL_DR_SHIFT; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (mag3110_samp_freq[i][0] > 0) 16862306a36Sopenharmony_ci ret = 1000 / mag3110_samp_freq[i][0]; 16962306a36Sopenharmony_ci else 17062306a36Sopenharmony_ci ret = 1000; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return ret == 0 ? 1 : ret; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic int mag3110_standby(struct mag3110_data *data) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1, 17862306a36Sopenharmony_ci data->ctrl_reg1 & ~MAG3110_CTRL_AC); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic int mag3110_wait_standby(struct mag3110_data *data) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci int ret, tries = 30; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* 18662306a36Sopenharmony_ci * Takes up to 1/ODR to come out of active mode into stby 18762306a36Sopenharmony_ci * Longest expected period is 12.5seconds. 18862306a36Sopenharmony_ci * We'll sleep for 500ms between checks 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_ci while (tries-- > 0) { 19162306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, MAG3110_SYSMOD); 19262306a36Sopenharmony_ci if (ret < 0) { 19362306a36Sopenharmony_ci dev_err(&data->client->dev, "i2c error\n"); 19462306a36Sopenharmony_ci return ret; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci /* wait for standby */ 19762306a36Sopenharmony_ci if ((ret & MAG3110_SYSMOD_MODE_MASK) == 0) 19862306a36Sopenharmony_ci break; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci msleep_interruptible(500); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (tries < 0) { 20462306a36Sopenharmony_ci dev_err(&data->client->dev, "device not entering standby mode\n"); 20562306a36Sopenharmony_ci return -EIO; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return 0; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic int mag3110_active(struct mag3110_data *data) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1, 21462306a36Sopenharmony_ci data->ctrl_reg1); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci/* returns >0 if active, 0 if in standby and <0 on error */ 21862306a36Sopenharmony_cistatic int mag3110_is_active(struct mag3110_data *data) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci int reg; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci reg = i2c_smbus_read_byte_data(data->client, MAG3110_CTRL_REG1); 22362306a36Sopenharmony_ci if (reg < 0) 22462306a36Sopenharmony_ci return reg; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return reg & MAG3110_CTRL_AC; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic int mag3110_change_config(struct mag3110_data *data, u8 reg, u8 val) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci int ret; 23262306a36Sopenharmony_ci int is_active; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci mutex_lock(&data->lock); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci is_active = mag3110_is_active(data); 23762306a36Sopenharmony_ci if (is_active < 0) { 23862306a36Sopenharmony_ci ret = is_active; 23962306a36Sopenharmony_ci goto fail; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* config can only be changed when in standby */ 24362306a36Sopenharmony_ci if (is_active > 0) { 24462306a36Sopenharmony_ci ret = mag3110_standby(data); 24562306a36Sopenharmony_ci if (ret < 0) 24662306a36Sopenharmony_ci goto fail; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* 25062306a36Sopenharmony_ci * After coming out of active we must wait for the part 25162306a36Sopenharmony_ci * to transition to STBY. This can take up to 1 /ODR to occur 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_ci ret = mag3110_wait_standby(data); 25462306a36Sopenharmony_ci if (ret < 0) 25562306a36Sopenharmony_ci goto fail; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, reg, val); 25862306a36Sopenharmony_ci if (ret < 0) 25962306a36Sopenharmony_ci goto fail; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (is_active > 0) { 26262306a36Sopenharmony_ci ret = mag3110_active(data); 26362306a36Sopenharmony_ci if (ret < 0) 26462306a36Sopenharmony_ci goto fail; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci ret = 0; 26862306a36Sopenharmony_cifail: 26962306a36Sopenharmony_ci mutex_unlock(&data->lock); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return ret; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic int mag3110_read_raw(struct iio_dev *indio_dev, 27562306a36Sopenharmony_ci struct iio_chan_spec const *chan, 27662306a36Sopenharmony_ci int *val, int *val2, long mask) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct mag3110_data *data = iio_priv(indio_dev); 27962306a36Sopenharmony_ci __be16 buffer[3]; 28062306a36Sopenharmony_ci int i, ret; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci switch (mask) { 28362306a36Sopenharmony_ci case IIO_CHAN_INFO_RAW: 28462306a36Sopenharmony_ci ret = iio_device_claim_direct_mode(indio_dev); 28562306a36Sopenharmony_ci if (ret) 28662306a36Sopenharmony_ci return ret; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci switch (chan->type) { 28962306a36Sopenharmony_ci case IIO_MAGN: /* in 0.1 uT / LSB */ 29062306a36Sopenharmony_ci ret = mag3110_read(data, buffer); 29162306a36Sopenharmony_ci if (ret < 0) 29262306a36Sopenharmony_ci goto release; 29362306a36Sopenharmony_ci *val = sign_extend32( 29462306a36Sopenharmony_ci be16_to_cpu(buffer[chan->scan_index]), 29562306a36Sopenharmony_ci chan->scan_type.realbits - 1); 29662306a36Sopenharmony_ci ret = IIO_VAL_INT; 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci case IIO_TEMP: /* in 1 C / LSB */ 29962306a36Sopenharmony_ci mutex_lock(&data->lock); 30062306a36Sopenharmony_ci ret = mag3110_request(data); 30162306a36Sopenharmony_ci if (ret < 0) { 30262306a36Sopenharmony_ci mutex_unlock(&data->lock); 30362306a36Sopenharmony_ci goto release; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, 30662306a36Sopenharmony_ci MAG3110_DIE_TEMP); 30762306a36Sopenharmony_ci mutex_unlock(&data->lock); 30862306a36Sopenharmony_ci if (ret < 0) 30962306a36Sopenharmony_ci goto release; 31062306a36Sopenharmony_ci *val = sign_extend32(ret, 31162306a36Sopenharmony_ci chan->scan_type.realbits - 1); 31262306a36Sopenharmony_ci ret = IIO_VAL_INT; 31362306a36Sopenharmony_ci break; 31462306a36Sopenharmony_ci default: 31562306a36Sopenharmony_ci ret = -EINVAL; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_cirelease: 31862306a36Sopenharmony_ci iio_device_release_direct_mode(indio_dev); 31962306a36Sopenharmony_ci return ret; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 32262306a36Sopenharmony_ci switch (chan->type) { 32362306a36Sopenharmony_ci case IIO_MAGN: 32462306a36Sopenharmony_ci *val = 0; 32562306a36Sopenharmony_ci *val2 = 1000; 32662306a36Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 32762306a36Sopenharmony_ci case IIO_TEMP: 32862306a36Sopenharmony_ci *val = 1000; 32962306a36Sopenharmony_ci return IIO_VAL_INT; 33062306a36Sopenharmony_ci default: 33162306a36Sopenharmony_ci return -EINVAL; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: 33462306a36Sopenharmony_ci i = data->ctrl_reg1 >> MAG3110_CTRL_DR_SHIFT; 33562306a36Sopenharmony_ci *val = mag3110_samp_freq[i][0]; 33662306a36Sopenharmony_ci *val2 = mag3110_samp_freq[i][1]; 33762306a36Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 33862306a36Sopenharmony_ci case IIO_CHAN_INFO_CALIBBIAS: 33962306a36Sopenharmony_ci ret = i2c_smbus_read_word_swapped(data->client, 34062306a36Sopenharmony_ci MAG3110_OFF_X + 2 * chan->scan_index); 34162306a36Sopenharmony_ci if (ret < 0) 34262306a36Sopenharmony_ci return ret; 34362306a36Sopenharmony_ci *val = sign_extend32(ret >> 1, 14); 34462306a36Sopenharmony_ci return IIO_VAL_INT; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci return -EINVAL; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic int mag3110_write_raw(struct iio_dev *indio_dev, 35062306a36Sopenharmony_ci struct iio_chan_spec const *chan, 35162306a36Sopenharmony_ci int val, int val2, long mask) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct mag3110_data *data = iio_priv(indio_dev); 35462306a36Sopenharmony_ci int rate, ret; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci ret = iio_device_claim_direct_mode(indio_dev); 35762306a36Sopenharmony_ci if (ret) 35862306a36Sopenharmony_ci return ret; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci switch (mask) { 36162306a36Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: 36262306a36Sopenharmony_ci rate = mag3110_get_samp_freq_index(data, val, val2); 36362306a36Sopenharmony_ci if (rate < 0) { 36462306a36Sopenharmony_ci ret = -EINVAL; 36562306a36Sopenharmony_ci break; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci data->ctrl_reg1 &= 0xff & ~MAG3110_CTRL_DR_MASK 36862306a36Sopenharmony_ci & ~MAG3110_CTRL_AC; 36962306a36Sopenharmony_ci data->ctrl_reg1 |= rate << MAG3110_CTRL_DR_SHIFT; 37062306a36Sopenharmony_ci data->sleep_val = mag3110_calculate_sleep(data); 37162306a36Sopenharmony_ci if (data->sleep_val < 40) 37262306a36Sopenharmony_ci data->ctrl_reg1 |= MAG3110_CTRL_AC; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci ret = mag3110_change_config(data, MAG3110_CTRL_REG1, 37562306a36Sopenharmony_ci data->ctrl_reg1); 37662306a36Sopenharmony_ci break; 37762306a36Sopenharmony_ci case IIO_CHAN_INFO_CALIBBIAS: 37862306a36Sopenharmony_ci if (val < -10000 || val > 10000) { 37962306a36Sopenharmony_ci ret = -EINVAL; 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci ret = i2c_smbus_write_word_swapped(data->client, 38362306a36Sopenharmony_ci MAG3110_OFF_X + 2 * chan->scan_index, val << 1); 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci default: 38662306a36Sopenharmony_ci ret = -EINVAL; 38762306a36Sopenharmony_ci break; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci iio_device_release_direct_mode(indio_dev); 39062306a36Sopenharmony_ci return ret; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic irqreturn_t mag3110_trigger_handler(int irq, void *p) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct iio_poll_func *pf = p; 39662306a36Sopenharmony_ci struct iio_dev *indio_dev = pf->indio_dev; 39762306a36Sopenharmony_ci struct mag3110_data *data = iio_priv(indio_dev); 39862306a36Sopenharmony_ci int ret; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci ret = mag3110_read(data, data->scan.channels); 40162306a36Sopenharmony_ci if (ret < 0) 40262306a36Sopenharmony_ci goto done; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (test_bit(3, indio_dev->active_scan_mask)) { 40562306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, 40662306a36Sopenharmony_ci MAG3110_DIE_TEMP); 40762306a36Sopenharmony_ci if (ret < 0) 40862306a36Sopenharmony_ci goto done; 40962306a36Sopenharmony_ci data->scan.temperature = ret; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, 41362306a36Sopenharmony_ci iio_get_time_ns(indio_dev)); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cidone: 41662306a36Sopenharmony_ci iio_trigger_notify_done(indio_dev->trig); 41762306a36Sopenharmony_ci return IRQ_HANDLED; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci#define MAG3110_CHANNEL(axis, idx) { \ 42162306a36Sopenharmony_ci .type = IIO_MAGN, \ 42262306a36Sopenharmony_ci .modified = 1, \ 42362306a36Sopenharmony_ci .channel2 = IIO_MOD_##axis, \ 42462306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ 42562306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBBIAS), \ 42662306a36Sopenharmony_ci .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ 42762306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), \ 42862306a36Sopenharmony_ci .scan_index = idx, \ 42962306a36Sopenharmony_ci .scan_type = { \ 43062306a36Sopenharmony_ci .sign = 's', \ 43162306a36Sopenharmony_ci .realbits = 16, \ 43262306a36Sopenharmony_ci .storagebits = 16, \ 43362306a36Sopenharmony_ci .endianness = IIO_BE, \ 43462306a36Sopenharmony_ci }, \ 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic const struct iio_chan_spec mag3110_channels[] = { 43862306a36Sopenharmony_ci MAG3110_CHANNEL(X, 0), 43962306a36Sopenharmony_ci MAG3110_CHANNEL(Y, 1), 44062306a36Sopenharmony_ci MAG3110_CHANNEL(Z, 2), 44162306a36Sopenharmony_ci { 44262306a36Sopenharmony_ci .type = IIO_TEMP, 44362306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 44462306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 44562306a36Sopenharmony_ci .scan_index = 3, 44662306a36Sopenharmony_ci .scan_type = { 44762306a36Sopenharmony_ci .sign = 's', 44862306a36Sopenharmony_ci .realbits = 8, 44962306a36Sopenharmony_ci .storagebits = 8, 45062306a36Sopenharmony_ci }, 45162306a36Sopenharmony_ci }, 45262306a36Sopenharmony_ci IIO_CHAN_SOFT_TIMESTAMP(4), 45362306a36Sopenharmony_ci}; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic struct attribute *mag3110_attributes[] = { 45662306a36Sopenharmony_ci &iio_dev_attr_sampling_frequency_available.dev_attr.attr, 45762306a36Sopenharmony_ci NULL 45862306a36Sopenharmony_ci}; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic const struct attribute_group mag3110_group = { 46162306a36Sopenharmony_ci .attrs = mag3110_attributes, 46262306a36Sopenharmony_ci}; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic const struct iio_info mag3110_info = { 46562306a36Sopenharmony_ci .attrs = &mag3110_group, 46662306a36Sopenharmony_ci .read_raw = &mag3110_read_raw, 46762306a36Sopenharmony_ci .write_raw = &mag3110_write_raw, 46862306a36Sopenharmony_ci}; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic const unsigned long mag3110_scan_masks[] = {0x7, 0xf, 0}; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic int mag3110_probe(struct i2c_client *client) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci const struct i2c_device_id *id = i2c_client_get_device_id(client); 47562306a36Sopenharmony_ci struct mag3110_data *data; 47662306a36Sopenharmony_ci struct iio_dev *indio_dev; 47762306a36Sopenharmony_ci int ret; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 48062306a36Sopenharmony_ci if (!indio_dev) 48162306a36Sopenharmony_ci return -ENOMEM; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci data = iio_priv(indio_dev); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci data->vdd_reg = devm_regulator_get(&client->dev, "vdd"); 48662306a36Sopenharmony_ci if (IS_ERR(data->vdd_reg)) 48762306a36Sopenharmony_ci return dev_err_probe(&client->dev, PTR_ERR(data->vdd_reg), 48862306a36Sopenharmony_ci "failed to get VDD regulator!\n"); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci data->vddio_reg = devm_regulator_get(&client->dev, "vddio"); 49162306a36Sopenharmony_ci if (IS_ERR(data->vddio_reg)) 49262306a36Sopenharmony_ci return dev_err_probe(&client->dev, PTR_ERR(data->vddio_reg), 49362306a36Sopenharmony_ci "failed to get VDDIO regulator!\n"); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci ret = regulator_enable(data->vdd_reg); 49662306a36Sopenharmony_ci if (ret) { 49762306a36Sopenharmony_ci dev_err(&client->dev, "failed to enable VDD regulator!\n"); 49862306a36Sopenharmony_ci return ret; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci ret = regulator_enable(data->vddio_reg); 50262306a36Sopenharmony_ci if (ret) { 50362306a36Sopenharmony_ci dev_err(&client->dev, "failed to enable VDDIO regulator!\n"); 50462306a36Sopenharmony_ci goto disable_regulator_vdd; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(client, MAG3110_WHO_AM_I); 50862306a36Sopenharmony_ci if (ret < 0) 50962306a36Sopenharmony_ci goto disable_regulators; 51062306a36Sopenharmony_ci if (ret != MAG3110_DEVICE_ID) { 51162306a36Sopenharmony_ci ret = -ENODEV; 51262306a36Sopenharmony_ci goto disable_regulators; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci data->client = client; 51662306a36Sopenharmony_ci mutex_init(&data->lock); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 51962306a36Sopenharmony_ci indio_dev->info = &mag3110_info; 52062306a36Sopenharmony_ci indio_dev->name = id->name; 52162306a36Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 52262306a36Sopenharmony_ci indio_dev->channels = mag3110_channels; 52362306a36Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(mag3110_channels); 52462306a36Sopenharmony_ci indio_dev->available_scan_masks = mag3110_scan_masks; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci data->ctrl_reg1 = MAG3110_CTRL_DR_DEFAULT << MAG3110_CTRL_DR_SHIFT; 52762306a36Sopenharmony_ci data->sleep_val = mag3110_calculate_sleep(data); 52862306a36Sopenharmony_ci if (data->sleep_val < 40) 52962306a36Sopenharmony_ci data->ctrl_reg1 |= MAG3110_CTRL_AC; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci ret = mag3110_change_config(data, MAG3110_CTRL_REG1, data->ctrl_reg1); 53262306a36Sopenharmony_ci if (ret < 0) 53362306a36Sopenharmony_ci goto disable_regulators; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, MAG3110_CTRL_REG2, 53662306a36Sopenharmony_ci MAG3110_CTRL_AUTO_MRST_EN); 53762306a36Sopenharmony_ci if (ret < 0) 53862306a36Sopenharmony_ci goto standby_on_error; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci ret = iio_triggered_buffer_setup(indio_dev, NULL, 54162306a36Sopenharmony_ci mag3110_trigger_handler, NULL); 54262306a36Sopenharmony_ci if (ret < 0) 54362306a36Sopenharmony_ci goto standby_on_error; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci ret = iio_device_register(indio_dev); 54662306a36Sopenharmony_ci if (ret < 0) 54762306a36Sopenharmony_ci goto buffer_cleanup; 54862306a36Sopenharmony_ci return 0; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cibuffer_cleanup: 55162306a36Sopenharmony_ci iio_triggered_buffer_cleanup(indio_dev); 55262306a36Sopenharmony_cistandby_on_error: 55362306a36Sopenharmony_ci mag3110_standby(iio_priv(indio_dev)); 55462306a36Sopenharmony_cidisable_regulators: 55562306a36Sopenharmony_ci regulator_disable(data->vddio_reg); 55662306a36Sopenharmony_cidisable_regulator_vdd: 55762306a36Sopenharmony_ci regulator_disable(data->vdd_reg); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return ret; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic void mag3110_remove(struct i2c_client *client) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(client); 56562306a36Sopenharmony_ci struct mag3110_data *data = iio_priv(indio_dev); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci iio_device_unregister(indio_dev); 56862306a36Sopenharmony_ci iio_triggered_buffer_cleanup(indio_dev); 56962306a36Sopenharmony_ci mag3110_standby(iio_priv(indio_dev)); 57062306a36Sopenharmony_ci regulator_disable(data->vddio_reg); 57162306a36Sopenharmony_ci regulator_disable(data->vdd_reg); 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic int mag3110_suspend(struct device *dev) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct mag3110_data *data = iio_priv(i2c_get_clientdata( 57762306a36Sopenharmony_ci to_i2c_client(dev))); 57862306a36Sopenharmony_ci int ret; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci ret = mag3110_standby(iio_priv(i2c_get_clientdata( 58162306a36Sopenharmony_ci to_i2c_client(dev)))); 58262306a36Sopenharmony_ci if (ret) 58362306a36Sopenharmony_ci return ret; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci ret = regulator_disable(data->vddio_reg); 58662306a36Sopenharmony_ci if (ret) { 58762306a36Sopenharmony_ci dev_err(dev, "failed to disable VDDIO regulator\n"); 58862306a36Sopenharmony_ci return ret; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci ret = regulator_disable(data->vdd_reg); 59262306a36Sopenharmony_ci if (ret) { 59362306a36Sopenharmony_ci dev_err(dev, "failed to disable VDD regulator\n"); 59462306a36Sopenharmony_ci return ret; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci return 0; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic int mag3110_resume(struct device *dev) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci struct mag3110_data *data = iio_priv(i2c_get_clientdata( 60362306a36Sopenharmony_ci to_i2c_client(dev))); 60462306a36Sopenharmony_ci int ret; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci ret = regulator_enable(data->vdd_reg); 60762306a36Sopenharmony_ci if (ret) { 60862306a36Sopenharmony_ci dev_err(dev, "failed to enable VDD regulator\n"); 60962306a36Sopenharmony_ci return ret; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci ret = regulator_enable(data->vddio_reg); 61362306a36Sopenharmony_ci if (ret) { 61462306a36Sopenharmony_ci dev_err(dev, "failed to enable VDDIO regulator\n"); 61562306a36Sopenharmony_ci regulator_disable(data->vdd_reg); 61662306a36Sopenharmony_ci return ret; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1, 62062306a36Sopenharmony_ci data->ctrl_reg1); 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(mag3110_pm_ops, mag3110_suspend, 62462306a36Sopenharmony_ci mag3110_resume); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_cistatic const struct i2c_device_id mag3110_id[] = { 62762306a36Sopenharmony_ci { "mag3110", 0 }, 62862306a36Sopenharmony_ci { } 62962306a36Sopenharmony_ci}; 63062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mag3110_id); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic const struct of_device_id mag3110_of_match[] = { 63362306a36Sopenharmony_ci { .compatible = "fsl,mag3110" }, 63462306a36Sopenharmony_ci { } 63562306a36Sopenharmony_ci}; 63662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mag3110_of_match); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic struct i2c_driver mag3110_driver = { 63962306a36Sopenharmony_ci .driver = { 64062306a36Sopenharmony_ci .name = "mag3110", 64162306a36Sopenharmony_ci .of_match_table = mag3110_of_match, 64262306a36Sopenharmony_ci .pm = pm_sleep_ptr(&mag3110_pm_ops), 64362306a36Sopenharmony_ci }, 64462306a36Sopenharmony_ci .probe = mag3110_probe, 64562306a36Sopenharmony_ci .remove = mag3110_remove, 64662306a36Sopenharmony_ci .id_table = mag3110_id, 64762306a36Sopenharmony_ci}; 64862306a36Sopenharmony_cimodule_i2c_driver(mag3110_driver); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ciMODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 65162306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale MAG3110 magnetometer driver"); 65262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 653