136edc939SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
239631b5fSPeter Meerwald /*
339631b5fSPeter Meerwald * mag3110.c - Support for Freescale MAG3110 magnetometer sensor
439631b5fSPeter Meerwald *
539631b5fSPeter Meerwald * Copyright (c) 2013 Peter Meerwald <pmeerw@pmeerw.net>
639631b5fSPeter Meerwald *
739631b5fSPeter Meerwald * (7-bit I2C slave address 0x0e)
839631b5fSPeter Meerwald *
939631b5fSPeter Meerwald * TODO: irq, user offset, oversampling, continuous mode
1039631b5fSPeter Meerwald */
1139631b5fSPeter Meerwald
1239631b5fSPeter Meerwald #include <linux/module.h>
1339631b5fSPeter Meerwald #include <linux/i2c.h>
1439631b5fSPeter Meerwald #include <linux/iio/iio.h>
1539631b5fSPeter Meerwald #include <linux/iio/sysfs.h>
1639631b5fSPeter Meerwald #include <linux/iio/trigger_consumer.h>
1739631b5fSPeter Meerwald #include <linux/iio/buffer.h>
1839631b5fSPeter Meerwald #include <linux/iio/triggered_buffer.h>
1939631b5fSPeter Meerwald #include <linux/delay.h>
201dca9bdeSAnson Huang #include <linux/regulator/consumer.h>
2139631b5fSPeter Meerwald
2239631b5fSPeter Meerwald #define MAG3110_STATUS 0x00
2339631b5fSPeter Meerwald #define MAG3110_OUT_X 0x01 /* MSB first */
2439631b5fSPeter Meerwald #define MAG3110_OUT_Y 0x03
2539631b5fSPeter Meerwald #define MAG3110_OUT_Z 0x05
2639631b5fSPeter Meerwald #define MAG3110_WHO_AM_I 0x07
273345d470SRichard Tresidder #define MAG3110_SYSMOD 0x08
2839631b5fSPeter Meerwald #define MAG3110_OFF_X 0x09 /* MSB first */
2939631b5fSPeter Meerwald #define MAG3110_OFF_Y 0x0b
3039631b5fSPeter Meerwald #define MAG3110_OFF_Z 0x0d
3139631b5fSPeter Meerwald #define MAG3110_DIE_TEMP 0x0f
3239631b5fSPeter Meerwald #define MAG3110_CTRL_REG1 0x10
3339631b5fSPeter Meerwald #define MAG3110_CTRL_REG2 0x11
3439631b5fSPeter Meerwald
3539631b5fSPeter Meerwald #define MAG3110_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
3639631b5fSPeter Meerwald
3739631b5fSPeter Meerwald #define MAG3110_CTRL_DR_MASK (BIT(7) | BIT(6) | BIT(5))
3839631b5fSPeter Meerwald #define MAG3110_CTRL_DR_SHIFT 5
3939631b5fSPeter Meerwald #define MAG3110_CTRL_DR_DEFAULT 0
4039631b5fSPeter Meerwald
413345d470SRichard Tresidder #define MAG3110_SYSMOD_MODE_MASK GENMASK(1, 0)
423345d470SRichard Tresidder
4339631b5fSPeter Meerwald #define MAG3110_CTRL_TM BIT(1) /* trigger single measurement */
4439631b5fSPeter Meerwald #define MAG3110_CTRL_AC BIT(0) /* continuous measurements */
4539631b5fSPeter Meerwald
4639631b5fSPeter Meerwald #define MAG3110_CTRL_AUTO_MRST_EN BIT(7) /* magnetic auto-reset */
4739631b5fSPeter Meerwald #define MAG3110_CTRL_RAW BIT(5) /* measurements not user-offset corrected */
4839631b5fSPeter Meerwald
4939631b5fSPeter Meerwald #define MAG3110_DEVICE_ID 0xc4
5039631b5fSPeter Meerwald
5139631b5fSPeter Meerwald /* Each client has this additional data */
5239631b5fSPeter Meerwald struct mag3110_data {
5339631b5fSPeter Meerwald struct i2c_client *client;
5439631b5fSPeter Meerwald struct mutex lock;
5539631b5fSPeter Meerwald u8 ctrl_reg1;
563345d470SRichard Tresidder int sleep_val;
571dca9bdeSAnson Huang struct regulator *vdd_reg;
581dca9bdeSAnson Huang struct regulator *vddio_reg;
5989deb133SJonathan Cameron /* Ensure natural alignment of timestamp */
6089deb133SJonathan Cameron struct {
6189deb133SJonathan Cameron __be16 channels[3];
6289deb133SJonathan Cameron u8 temperature;
6389deb133SJonathan Cameron s64 ts __aligned(8);
6489deb133SJonathan Cameron } scan;
6539631b5fSPeter Meerwald };
6639631b5fSPeter Meerwald
mag3110_request(struct mag3110_data * data)6739631b5fSPeter Meerwald static int mag3110_request(struct mag3110_data *data)
6839631b5fSPeter Meerwald {
6939631b5fSPeter Meerwald int ret, tries = 150;
7039631b5fSPeter Meerwald
713345d470SRichard Tresidder if ((data->ctrl_reg1 & MAG3110_CTRL_AC) == 0) {
7239631b5fSPeter Meerwald /* trigger measurement */
7339631b5fSPeter Meerwald ret = i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
7439631b5fSPeter Meerwald data->ctrl_reg1 | MAG3110_CTRL_TM);
7539631b5fSPeter Meerwald if (ret < 0)
7639631b5fSPeter Meerwald return ret;
773345d470SRichard Tresidder }
7839631b5fSPeter Meerwald
7939631b5fSPeter Meerwald while (tries-- > 0) {
8039631b5fSPeter Meerwald ret = i2c_smbus_read_byte_data(data->client, MAG3110_STATUS);
8139631b5fSPeter Meerwald if (ret < 0)
8239631b5fSPeter Meerwald return ret;
8339631b5fSPeter Meerwald /* wait for data ready */
8439631b5fSPeter Meerwald if ((ret & MAG3110_STATUS_DRDY) == MAG3110_STATUS_DRDY)
8539631b5fSPeter Meerwald break;
863345d470SRichard Tresidder
873345d470SRichard Tresidder if (data->sleep_val <= 20)
883345d470SRichard Tresidder usleep_range(data->sleep_val * 250, data->sleep_val * 500);
893345d470SRichard Tresidder else
9039631b5fSPeter Meerwald msleep(20);
9139631b5fSPeter Meerwald }
9239631b5fSPeter Meerwald
9339631b5fSPeter Meerwald if (tries < 0) {
9439631b5fSPeter Meerwald dev_err(&data->client->dev, "data not ready\n");
9539631b5fSPeter Meerwald return -EIO;
9639631b5fSPeter Meerwald }
9739631b5fSPeter Meerwald
9839631b5fSPeter Meerwald return 0;
9939631b5fSPeter Meerwald }
10039631b5fSPeter Meerwald
mag3110_read(struct mag3110_data * data,__be16 buf[3])10139631b5fSPeter Meerwald static int mag3110_read(struct mag3110_data *data, __be16 buf[3])
10239631b5fSPeter Meerwald {
10339631b5fSPeter Meerwald int ret;
10439631b5fSPeter Meerwald
10539631b5fSPeter Meerwald mutex_lock(&data->lock);
10639631b5fSPeter Meerwald ret = mag3110_request(data);
10739631b5fSPeter Meerwald if (ret < 0) {
10839631b5fSPeter Meerwald mutex_unlock(&data->lock);
10939631b5fSPeter Meerwald return ret;
11039631b5fSPeter Meerwald }
11139631b5fSPeter Meerwald ret = i2c_smbus_read_i2c_block_data(data->client,
11239631b5fSPeter Meerwald MAG3110_OUT_X, 3 * sizeof(__be16), (u8 *) buf);
11339631b5fSPeter Meerwald mutex_unlock(&data->lock);
11439631b5fSPeter Meerwald
11539631b5fSPeter Meerwald return ret;
11639631b5fSPeter Meerwald }
11739631b5fSPeter Meerwald
mag3110_show_int_plus_micros(char * buf,const int (* vals)[2],int n)11839631b5fSPeter Meerwald static ssize_t mag3110_show_int_plus_micros(char *buf,
11939631b5fSPeter Meerwald const int (*vals)[2], int n)
12039631b5fSPeter Meerwald {
12139631b5fSPeter Meerwald size_t len = 0;
12239631b5fSPeter Meerwald
12339631b5fSPeter Meerwald while (n-- > 0)
12439631b5fSPeter Meerwald len += scnprintf(buf + len, PAGE_SIZE - len,
12571bd8945SPeter Meerwald "%d.%06d ", vals[n][0], vals[n][1]);
12639631b5fSPeter Meerwald
12739631b5fSPeter Meerwald /* replace trailing space by newline */
12839631b5fSPeter Meerwald buf[len - 1] = '\n';
12939631b5fSPeter Meerwald
13039631b5fSPeter Meerwald return len;
13139631b5fSPeter Meerwald }
13239631b5fSPeter Meerwald
mag3110_get_int_plus_micros_index(const int (* vals)[2],int n,int val,int val2)13339631b5fSPeter Meerwald static int mag3110_get_int_plus_micros_index(const int (*vals)[2], int n,
13439631b5fSPeter Meerwald int val, int val2)
13539631b5fSPeter Meerwald {
13639631b5fSPeter Meerwald while (n-- > 0)
13739631b5fSPeter Meerwald if (val == vals[n][0] && val2 == vals[n][1])
13839631b5fSPeter Meerwald return n;
13939631b5fSPeter Meerwald
14039631b5fSPeter Meerwald return -EINVAL;
14139631b5fSPeter Meerwald }
14239631b5fSPeter Meerwald
14339631b5fSPeter Meerwald static const int mag3110_samp_freq[8][2] = {
14439631b5fSPeter Meerwald {80, 0}, {40, 0}, {20, 0}, {10, 0}, {5, 0}, {2, 500000},
14539631b5fSPeter Meerwald {1, 250000}, {0, 625000}
14639631b5fSPeter Meerwald };
14739631b5fSPeter Meerwald
mag3110_show_samp_freq_avail(struct device * dev,struct device_attribute * attr,char * buf)14839631b5fSPeter Meerwald static ssize_t mag3110_show_samp_freq_avail(struct device *dev,
14939631b5fSPeter Meerwald struct device_attribute *attr, char *buf)
15039631b5fSPeter Meerwald {
15139631b5fSPeter Meerwald return mag3110_show_int_plus_micros(buf, mag3110_samp_freq, 8);
15239631b5fSPeter Meerwald }
15339631b5fSPeter Meerwald
15439631b5fSPeter Meerwald static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mag3110_show_samp_freq_avail);
15539631b5fSPeter Meerwald
mag3110_get_samp_freq_index(struct mag3110_data * data,int val,int val2)15639631b5fSPeter Meerwald static int mag3110_get_samp_freq_index(struct mag3110_data *data,
15739631b5fSPeter Meerwald int val, int val2)
15839631b5fSPeter Meerwald {
15939631b5fSPeter Meerwald return mag3110_get_int_plus_micros_index(mag3110_samp_freq, 8, val,
16039631b5fSPeter Meerwald val2);
16139631b5fSPeter Meerwald }
16239631b5fSPeter Meerwald
mag3110_calculate_sleep(struct mag3110_data * data)1633345d470SRichard Tresidder static int mag3110_calculate_sleep(struct mag3110_data *data)
1643345d470SRichard Tresidder {
1653345d470SRichard Tresidder int ret, i = data->ctrl_reg1 >> MAG3110_CTRL_DR_SHIFT;
1663345d470SRichard Tresidder
1673345d470SRichard Tresidder if (mag3110_samp_freq[i][0] > 0)
1683345d470SRichard Tresidder ret = 1000 / mag3110_samp_freq[i][0];
1693345d470SRichard Tresidder else
1703345d470SRichard Tresidder ret = 1000;
1713345d470SRichard Tresidder
1723345d470SRichard Tresidder return ret == 0 ? 1 : ret;
1733345d470SRichard Tresidder }
1743345d470SRichard Tresidder
mag3110_standby(struct mag3110_data * data)1753345d470SRichard Tresidder static int mag3110_standby(struct mag3110_data *data)
1763345d470SRichard Tresidder {
1773345d470SRichard Tresidder return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
1783345d470SRichard Tresidder data->ctrl_reg1 & ~MAG3110_CTRL_AC);
1793345d470SRichard Tresidder }
1803345d470SRichard Tresidder
mag3110_wait_standby(struct mag3110_data * data)1813345d470SRichard Tresidder static int mag3110_wait_standby(struct mag3110_data *data)
1823345d470SRichard Tresidder {
1833345d470SRichard Tresidder int ret, tries = 30;
1843345d470SRichard Tresidder
1853345d470SRichard Tresidder /*
1863345d470SRichard Tresidder * Takes up to 1/ODR to come out of active mode into stby
1873345d470SRichard Tresidder * Longest expected period is 12.5seconds.
1883345d470SRichard Tresidder * We'll sleep for 500ms between checks
1893345d470SRichard Tresidder */
1903345d470SRichard Tresidder while (tries-- > 0) {
1913345d470SRichard Tresidder ret = i2c_smbus_read_byte_data(data->client, MAG3110_SYSMOD);
1923345d470SRichard Tresidder if (ret < 0) {
1933345d470SRichard Tresidder dev_err(&data->client->dev, "i2c error\n");
1943345d470SRichard Tresidder return ret;
1953345d470SRichard Tresidder }
1963345d470SRichard Tresidder /* wait for standby */
1973345d470SRichard Tresidder if ((ret & MAG3110_SYSMOD_MODE_MASK) == 0)
1983345d470SRichard Tresidder break;
1993345d470SRichard Tresidder
2003345d470SRichard Tresidder msleep_interruptible(500);
2013345d470SRichard Tresidder }
2023345d470SRichard Tresidder
2033345d470SRichard Tresidder if (tries < 0) {
2043345d470SRichard Tresidder dev_err(&data->client->dev, "device not entering standby mode\n");
2053345d470SRichard Tresidder return -EIO;
2063345d470SRichard Tresidder }
2073345d470SRichard Tresidder
2083345d470SRichard Tresidder return 0;
2093345d470SRichard Tresidder }
2103345d470SRichard Tresidder
mag3110_active(struct mag3110_data * data)2113345d470SRichard Tresidder static int mag3110_active(struct mag3110_data *data)
2123345d470SRichard Tresidder {
2133345d470SRichard Tresidder return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
2143345d470SRichard Tresidder data->ctrl_reg1);
2153345d470SRichard Tresidder }
2163345d470SRichard Tresidder
2173345d470SRichard Tresidder /* returns >0 if active, 0 if in standby and <0 on error */
mag3110_is_active(struct mag3110_data * data)2183345d470SRichard Tresidder static int mag3110_is_active(struct mag3110_data *data)
2193345d470SRichard Tresidder {
2203345d470SRichard Tresidder int reg;
2213345d470SRichard Tresidder
2223345d470SRichard Tresidder reg = i2c_smbus_read_byte_data(data->client, MAG3110_CTRL_REG1);
2233345d470SRichard Tresidder if (reg < 0)
2243345d470SRichard Tresidder return reg;
2253345d470SRichard Tresidder
2263345d470SRichard Tresidder return reg & MAG3110_CTRL_AC;
2273345d470SRichard Tresidder }
2283345d470SRichard Tresidder
mag3110_change_config(struct mag3110_data * data,u8 reg,u8 val)2293345d470SRichard Tresidder static int mag3110_change_config(struct mag3110_data *data, u8 reg, u8 val)
2303345d470SRichard Tresidder {
2313345d470SRichard Tresidder int ret;
2323345d470SRichard Tresidder int is_active;
2333345d470SRichard Tresidder
2343345d470SRichard Tresidder mutex_lock(&data->lock);
2353345d470SRichard Tresidder
2363345d470SRichard Tresidder is_active = mag3110_is_active(data);
2373345d470SRichard Tresidder if (is_active < 0) {
2383345d470SRichard Tresidder ret = is_active;
2393345d470SRichard Tresidder goto fail;
2403345d470SRichard Tresidder }
2413345d470SRichard Tresidder
2423345d470SRichard Tresidder /* config can only be changed when in standby */
2433345d470SRichard Tresidder if (is_active > 0) {
2443345d470SRichard Tresidder ret = mag3110_standby(data);
2453345d470SRichard Tresidder if (ret < 0)
2463345d470SRichard Tresidder goto fail;
2473345d470SRichard Tresidder }
2483345d470SRichard Tresidder
2493345d470SRichard Tresidder /*
2503345d470SRichard Tresidder * After coming out of active we must wait for the part
2513345d470SRichard Tresidder * to transition to STBY. This can take up to 1 /ODR to occur
2523345d470SRichard Tresidder */
2533345d470SRichard Tresidder ret = mag3110_wait_standby(data);
2543345d470SRichard Tresidder if (ret < 0)
2553345d470SRichard Tresidder goto fail;
2563345d470SRichard Tresidder
2573345d470SRichard Tresidder ret = i2c_smbus_write_byte_data(data->client, reg, val);
2583345d470SRichard Tresidder if (ret < 0)
2593345d470SRichard Tresidder goto fail;
2603345d470SRichard Tresidder
2613345d470SRichard Tresidder if (is_active > 0) {
2623345d470SRichard Tresidder ret = mag3110_active(data);
2633345d470SRichard Tresidder if (ret < 0)
2643345d470SRichard Tresidder goto fail;
2653345d470SRichard Tresidder }
2663345d470SRichard Tresidder
2673345d470SRichard Tresidder ret = 0;
2683345d470SRichard Tresidder fail:
2693345d470SRichard Tresidder mutex_unlock(&data->lock);
2703345d470SRichard Tresidder
2713345d470SRichard Tresidder return ret;
2723345d470SRichard Tresidder }
2733345d470SRichard Tresidder
mag3110_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)27439631b5fSPeter Meerwald static int mag3110_read_raw(struct iio_dev *indio_dev,
27539631b5fSPeter Meerwald struct iio_chan_spec const *chan,
27639631b5fSPeter Meerwald int *val, int *val2, long mask)
27739631b5fSPeter Meerwald {
27839631b5fSPeter Meerwald struct mag3110_data *data = iio_priv(indio_dev);
27939631b5fSPeter Meerwald __be16 buffer[3];
28039631b5fSPeter Meerwald int i, ret;
28139631b5fSPeter Meerwald
28239631b5fSPeter Meerwald switch (mask) {
28339631b5fSPeter Meerwald case IIO_CHAN_INFO_RAW:
284197399dcSAlison Schofield ret = iio_device_claim_direct_mode(indio_dev);
285197399dcSAlison Schofield if (ret)
286197399dcSAlison Schofield return ret;
287f25330f6SPeter Meerwald
28839631b5fSPeter Meerwald switch (chan->type) {
28939631b5fSPeter Meerwald case IIO_MAGN: /* in 0.1 uT / LSB */
29039631b5fSPeter Meerwald ret = mag3110_read(data, buffer);
29139631b5fSPeter Meerwald if (ret < 0)
292197399dcSAlison Schofield goto release;
29339631b5fSPeter Meerwald *val = sign_extend32(
2944d57fb54SGwendal Grignou be16_to_cpu(buffer[chan->scan_index]),
2954d57fb54SGwendal Grignou chan->scan_type.realbits - 1);
296197399dcSAlison Schofield ret = IIO_VAL_INT;
297197399dcSAlison Schofield break;
29839631b5fSPeter Meerwald case IIO_TEMP: /* in 1 C / LSB */
29939631b5fSPeter Meerwald mutex_lock(&data->lock);
30039631b5fSPeter Meerwald ret = mag3110_request(data);
30139631b5fSPeter Meerwald if (ret < 0) {
30239631b5fSPeter Meerwald mutex_unlock(&data->lock);
303197399dcSAlison Schofield goto release;
30439631b5fSPeter Meerwald }
30539631b5fSPeter Meerwald ret = i2c_smbus_read_byte_data(data->client,
30639631b5fSPeter Meerwald MAG3110_DIE_TEMP);
30739631b5fSPeter Meerwald mutex_unlock(&data->lock);
30839631b5fSPeter Meerwald if (ret < 0)
309197399dcSAlison Schofield goto release;
3104d57fb54SGwendal Grignou *val = sign_extend32(ret,
3114d57fb54SGwendal Grignou chan->scan_type.realbits - 1);
312197399dcSAlison Schofield ret = IIO_VAL_INT;
313197399dcSAlison Schofield break;
31439631b5fSPeter Meerwald default:
315197399dcSAlison Schofield ret = -EINVAL;
31639631b5fSPeter Meerwald }
317197399dcSAlison Schofield release:
318197399dcSAlison Schofield iio_device_release_direct_mode(indio_dev);
319197399dcSAlison Schofield return ret;
320197399dcSAlison Schofield
32139631b5fSPeter Meerwald case IIO_CHAN_INFO_SCALE:
322f9279d3aSPeter Meerwald switch (chan->type) {
323f9279d3aSPeter Meerwald case IIO_MAGN:
32439631b5fSPeter Meerwald *val = 0;
32539631b5fSPeter Meerwald *val2 = 1000;
32639631b5fSPeter Meerwald return IIO_VAL_INT_PLUS_MICRO;
327f9279d3aSPeter Meerwald case IIO_TEMP:
328f9279d3aSPeter Meerwald *val = 1000;
329f9279d3aSPeter Meerwald return IIO_VAL_INT;
330f9279d3aSPeter Meerwald default:
331f9279d3aSPeter Meerwald return -EINVAL;
332f9279d3aSPeter Meerwald }
33339631b5fSPeter Meerwald case IIO_CHAN_INFO_SAMP_FREQ:
33439631b5fSPeter Meerwald i = data->ctrl_reg1 >> MAG3110_CTRL_DR_SHIFT;
33539631b5fSPeter Meerwald *val = mag3110_samp_freq[i][0];
33639631b5fSPeter Meerwald *val2 = mag3110_samp_freq[i][1];
33739631b5fSPeter Meerwald return IIO_VAL_INT_PLUS_MICRO;
3380828eddcSPeter Meerwald case IIO_CHAN_INFO_CALIBBIAS:
3390828eddcSPeter Meerwald ret = i2c_smbus_read_word_swapped(data->client,
3400828eddcSPeter Meerwald MAG3110_OFF_X + 2 * chan->scan_index);
3410828eddcSPeter Meerwald if (ret < 0)
3420828eddcSPeter Meerwald return ret;
3430828eddcSPeter Meerwald *val = sign_extend32(ret >> 1, 14);
3440828eddcSPeter Meerwald return IIO_VAL_INT;
34539631b5fSPeter Meerwald }
34639631b5fSPeter Meerwald return -EINVAL;
34739631b5fSPeter Meerwald }
34839631b5fSPeter Meerwald
mag3110_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)34939631b5fSPeter Meerwald static int mag3110_write_raw(struct iio_dev *indio_dev,
35039631b5fSPeter Meerwald struct iio_chan_spec const *chan,
35139631b5fSPeter Meerwald int val, int val2, long mask)
35239631b5fSPeter Meerwald {
35339631b5fSPeter Meerwald struct mag3110_data *data = iio_priv(indio_dev);
35480dea21fSAlison Schofield int rate, ret;
35539631b5fSPeter Meerwald
35680dea21fSAlison Schofield ret = iio_device_claim_direct_mode(indio_dev);
35780dea21fSAlison Schofield if (ret)
35880dea21fSAlison Schofield return ret;
359f25330f6SPeter Meerwald
36039631b5fSPeter Meerwald switch (mask) {
36139631b5fSPeter Meerwald case IIO_CHAN_INFO_SAMP_FREQ:
36239631b5fSPeter Meerwald rate = mag3110_get_samp_freq_index(data, val, val2);
36380dea21fSAlison Schofield if (rate < 0) {
36480dea21fSAlison Schofield ret = -EINVAL;
36580dea21fSAlison Schofield break;
36680dea21fSAlison Schofield }
3673345d470SRichard Tresidder data->ctrl_reg1 &= 0xff & ~MAG3110_CTRL_DR_MASK
3683345d470SRichard Tresidder & ~MAG3110_CTRL_AC;
36939631b5fSPeter Meerwald data->ctrl_reg1 |= rate << MAG3110_CTRL_DR_SHIFT;
3703345d470SRichard Tresidder data->sleep_val = mag3110_calculate_sleep(data);
3713345d470SRichard Tresidder if (data->sleep_val < 40)
3723345d470SRichard Tresidder data->ctrl_reg1 |= MAG3110_CTRL_AC;
3733345d470SRichard Tresidder
3743345d470SRichard Tresidder ret = mag3110_change_config(data, MAG3110_CTRL_REG1,
3753345d470SRichard Tresidder data->ctrl_reg1);
37680dea21fSAlison Schofield break;
3770828eddcSPeter Meerwald case IIO_CHAN_INFO_CALIBBIAS:
37880dea21fSAlison Schofield if (val < -10000 || val > 10000) {
37980dea21fSAlison Schofield ret = -EINVAL;
38080dea21fSAlison Schofield break;
38139631b5fSPeter Meerwald }
38280dea21fSAlison Schofield ret = i2c_smbus_write_word_swapped(data->client,
38380dea21fSAlison Schofield MAG3110_OFF_X + 2 * chan->scan_index, val << 1);
38480dea21fSAlison Schofield break;
38580dea21fSAlison Schofield default:
38680dea21fSAlison Schofield ret = -EINVAL;
38780dea21fSAlison Schofield break;
38880dea21fSAlison Schofield }
38980dea21fSAlison Schofield iio_device_release_direct_mode(indio_dev);
39080dea21fSAlison Schofield return ret;
39139631b5fSPeter Meerwald }
39239631b5fSPeter Meerwald
mag3110_trigger_handler(int irq,void * p)39339631b5fSPeter Meerwald static irqreturn_t mag3110_trigger_handler(int irq, void *p)
39439631b5fSPeter Meerwald {
39539631b5fSPeter Meerwald struct iio_poll_func *pf = p;
39639631b5fSPeter Meerwald struct iio_dev *indio_dev = pf->indio_dev;
39739631b5fSPeter Meerwald struct mag3110_data *data = iio_priv(indio_dev);
39839631b5fSPeter Meerwald int ret;
39939631b5fSPeter Meerwald
40089deb133SJonathan Cameron ret = mag3110_read(data, data->scan.channels);
40139631b5fSPeter Meerwald if (ret < 0)
40239631b5fSPeter Meerwald goto done;
40339631b5fSPeter Meerwald
40439631b5fSPeter Meerwald if (test_bit(3, indio_dev->active_scan_mask)) {
40539631b5fSPeter Meerwald ret = i2c_smbus_read_byte_data(data->client,
40639631b5fSPeter Meerwald MAG3110_DIE_TEMP);
40739631b5fSPeter Meerwald if (ret < 0)
40839631b5fSPeter Meerwald goto done;
40989deb133SJonathan Cameron data->scan.temperature = ret;
41039631b5fSPeter Meerwald }
41139631b5fSPeter Meerwald
41289deb133SJonathan Cameron iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,
413bc2b7dabSGregor Boirie iio_get_time_ns(indio_dev));
41439631b5fSPeter Meerwald
41539631b5fSPeter Meerwald done:
41639631b5fSPeter Meerwald iio_trigger_notify_done(indio_dev->trig);
41739631b5fSPeter Meerwald return IRQ_HANDLED;
41839631b5fSPeter Meerwald }
41939631b5fSPeter Meerwald
42039631b5fSPeter Meerwald #define MAG3110_CHANNEL(axis, idx) { \
42139631b5fSPeter Meerwald .type = IIO_MAGN, \
42239631b5fSPeter Meerwald .modified = 1, \
42339631b5fSPeter Meerwald .channel2 = IIO_MOD_##axis, \
4240828eddcSPeter Meerwald .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
4250828eddcSPeter Meerwald BIT(IIO_CHAN_INFO_CALIBBIAS), \
42639631b5fSPeter Meerwald .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
42739631b5fSPeter Meerwald BIT(IIO_CHAN_INFO_SCALE), \
42839631b5fSPeter Meerwald .scan_index = idx, \
429911bdc68SPeter Meerwald .scan_type = { \
430911bdc68SPeter Meerwald .sign = 's', \
431911bdc68SPeter Meerwald .realbits = 16, \
432911bdc68SPeter Meerwald .storagebits = 16, \
433911bdc68SPeter Meerwald .endianness = IIO_BE, \
434911bdc68SPeter Meerwald }, \
43539631b5fSPeter Meerwald }
43639631b5fSPeter Meerwald
43739631b5fSPeter Meerwald static const struct iio_chan_spec mag3110_channels[] = {
43839631b5fSPeter Meerwald MAG3110_CHANNEL(X, 0),
43939631b5fSPeter Meerwald MAG3110_CHANNEL(Y, 1),
44039631b5fSPeter Meerwald MAG3110_CHANNEL(Z, 2),
44139631b5fSPeter Meerwald {
44239631b5fSPeter Meerwald .type = IIO_TEMP,
443f9279d3aSPeter Meerwald .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
444f9279d3aSPeter Meerwald BIT(IIO_CHAN_INFO_SCALE),
44539631b5fSPeter Meerwald .scan_index = 3,
446e5687979SJonathan Cameron .scan_type = {
447e5687979SJonathan Cameron .sign = 's',
448e5687979SJonathan Cameron .realbits = 8,
449e5687979SJonathan Cameron .storagebits = 8,
450e5687979SJonathan Cameron },
45139631b5fSPeter Meerwald },
45239631b5fSPeter Meerwald IIO_CHAN_SOFT_TIMESTAMP(4),
45339631b5fSPeter Meerwald };
45439631b5fSPeter Meerwald
45539631b5fSPeter Meerwald static struct attribute *mag3110_attributes[] = {
45639631b5fSPeter Meerwald &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
45739631b5fSPeter Meerwald NULL
45839631b5fSPeter Meerwald };
45939631b5fSPeter Meerwald
46039631b5fSPeter Meerwald static const struct attribute_group mag3110_group = {
46139631b5fSPeter Meerwald .attrs = mag3110_attributes,
46239631b5fSPeter Meerwald };
46339631b5fSPeter Meerwald
46439631b5fSPeter Meerwald static const struct iio_info mag3110_info = {
46539631b5fSPeter Meerwald .attrs = &mag3110_group,
46639631b5fSPeter Meerwald .read_raw = &mag3110_read_raw,
46739631b5fSPeter Meerwald .write_raw = &mag3110_write_raw,
46839631b5fSPeter Meerwald };
46939631b5fSPeter Meerwald
47039631b5fSPeter Meerwald static const unsigned long mag3110_scan_masks[] = {0x7, 0xf, 0};
47139631b5fSPeter Meerwald
mag3110_probe(struct i2c_client * client)472cee51403SUwe Kleine-König static int mag3110_probe(struct i2c_client *client)
47339631b5fSPeter Meerwald {
474cee51403SUwe Kleine-König const struct i2c_device_id *id = i2c_client_get_device_id(client);
47539631b5fSPeter Meerwald struct mag3110_data *data;
47639631b5fSPeter Meerwald struct iio_dev *indio_dev;
47739631b5fSPeter Meerwald int ret;
47839631b5fSPeter Meerwald
47939631b5fSPeter Meerwald indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
48039631b5fSPeter Meerwald if (!indio_dev)
48139631b5fSPeter Meerwald return -ENOMEM;
48239631b5fSPeter Meerwald
48339631b5fSPeter Meerwald data = iio_priv(indio_dev);
4841dca9bdeSAnson Huang
4851dca9bdeSAnson Huang data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
4860d81951dSKrzysztof Kozlowski if (IS_ERR(data->vdd_reg))
4870d81951dSKrzysztof Kozlowski return dev_err_probe(&client->dev, PTR_ERR(data->vdd_reg),
4880d81951dSKrzysztof Kozlowski "failed to get VDD regulator!\n");
4891dca9bdeSAnson Huang
4901dca9bdeSAnson Huang data->vddio_reg = devm_regulator_get(&client->dev, "vddio");
4910d81951dSKrzysztof Kozlowski if (IS_ERR(data->vddio_reg))
4920d81951dSKrzysztof Kozlowski return dev_err_probe(&client->dev, PTR_ERR(data->vddio_reg),
4930d81951dSKrzysztof Kozlowski "failed to get VDDIO regulator!\n");
4941dca9bdeSAnson Huang
4951dca9bdeSAnson Huang ret = regulator_enable(data->vdd_reg);
4961dca9bdeSAnson Huang if (ret) {
4971dca9bdeSAnson Huang dev_err(&client->dev, "failed to enable VDD regulator!\n");
4981dca9bdeSAnson Huang return ret;
4991dca9bdeSAnson Huang }
5001dca9bdeSAnson Huang
5011dca9bdeSAnson Huang ret = regulator_enable(data->vddio_reg);
5021dca9bdeSAnson Huang if (ret) {
5031dca9bdeSAnson Huang dev_err(&client->dev, "failed to enable VDDIO regulator!\n");
5041dca9bdeSAnson Huang goto disable_regulator_vdd;
5051dca9bdeSAnson Huang }
5061dca9bdeSAnson Huang
5071dca9bdeSAnson Huang ret = i2c_smbus_read_byte_data(client, MAG3110_WHO_AM_I);
5081dca9bdeSAnson Huang if (ret < 0)
5091dca9bdeSAnson Huang goto disable_regulators;
5101dca9bdeSAnson Huang if (ret != MAG3110_DEVICE_ID) {
5111dca9bdeSAnson Huang ret = -ENODEV;
5121dca9bdeSAnson Huang goto disable_regulators;
5131dca9bdeSAnson Huang }
5141dca9bdeSAnson Huang
51539631b5fSPeter Meerwald data->client = client;
51639631b5fSPeter Meerwald mutex_init(&data->lock);
51739631b5fSPeter Meerwald
51839631b5fSPeter Meerwald i2c_set_clientdata(client, indio_dev);
51939631b5fSPeter Meerwald indio_dev->info = &mag3110_info;
52039631b5fSPeter Meerwald indio_dev->name = id->name;
52139631b5fSPeter Meerwald indio_dev->modes = INDIO_DIRECT_MODE;
52239631b5fSPeter Meerwald indio_dev->channels = mag3110_channels;
52339631b5fSPeter Meerwald indio_dev->num_channels = ARRAY_SIZE(mag3110_channels);
52439631b5fSPeter Meerwald indio_dev->available_scan_masks = mag3110_scan_masks;
52539631b5fSPeter Meerwald
5266584891bSPeter Meerwald data->ctrl_reg1 = MAG3110_CTRL_DR_DEFAULT << MAG3110_CTRL_DR_SHIFT;
5273345d470SRichard Tresidder data->sleep_val = mag3110_calculate_sleep(data);
5283345d470SRichard Tresidder if (data->sleep_val < 40)
5293345d470SRichard Tresidder data->ctrl_reg1 |= MAG3110_CTRL_AC;
5303345d470SRichard Tresidder
5313345d470SRichard Tresidder ret = mag3110_change_config(data, MAG3110_CTRL_REG1, data->ctrl_reg1);
53239631b5fSPeter Meerwald if (ret < 0)
5331dca9bdeSAnson Huang goto disable_regulators;
53439631b5fSPeter Meerwald
53539631b5fSPeter Meerwald ret = i2c_smbus_write_byte_data(client, MAG3110_CTRL_REG2,
5360828eddcSPeter Meerwald MAG3110_CTRL_AUTO_MRST_EN);
53739631b5fSPeter Meerwald if (ret < 0)
53866687e6aSCristina Opriceana goto standby_on_error;
53939631b5fSPeter Meerwald
54039631b5fSPeter Meerwald ret = iio_triggered_buffer_setup(indio_dev, NULL,
54139631b5fSPeter Meerwald mag3110_trigger_handler, NULL);
54239631b5fSPeter Meerwald if (ret < 0)
54366687e6aSCristina Opriceana goto standby_on_error;
54439631b5fSPeter Meerwald
54539631b5fSPeter Meerwald ret = iio_device_register(indio_dev);
54639631b5fSPeter Meerwald if (ret < 0)
54739631b5fSPeter Meerwald goto buffer_cleanup;
54839631b5fSPeter Meerwald return 0;
54939631b5fSPeter Meerwald
55039631b5fSPeter Meerwald buffer_cleanup:
55139631b5fSPeter Meerwald iio_triggered_buffer_cleanup(indio_dev);
55266687e6aSCristina Opriceana standby_on_error:
55366687e6aSCristina Opriceana mag3110_standby(iio_priv(indio_dev));
5541dca9bdeSAnson Huang disable_regulators:
5551dca9bdeSAnson Huang regulator_disable(data->vddio_reg);
5561dca9bdeSAnson Huang disable_regulator_vdd:
5571dca9bdeSAnson Huang regulator_disable(data->vdd_reg);
5581dca9bdeSAnson Huang
55939631b5fSPeter Meerwald return ret;
56039631b5fSPeter Meerwald }
56139631b5fSPeter Meerwald
mag3110_remove(struct i2c_client * client)562ed5c2f5fSUwe Kleine-König static void mag3110_remove(struct i2c_client *client)
56339631b5fSPeter Meerwald {
56439631b5fSPeter Meerwald struct iio_dev *indio_dev = i2c_get_clientdata(client);
5651dca9bdeSAnson Huang struct mag3110_data *data = iio_priv(indio_dev);
56639631b5fSPeter Meerwald
56739631b5fSPeter Meerwald iio_device_unregister(indio_dev);
56839631b5fSPeter Meerwald iio_triggered_buffer_cleanup(indio_dev);
56939631b5fSPeter Meerwald mag3110_standby(iio_priv(indio_dev));
5701dca9bdeSAnson Huang regulator_disable(data->vddio_reg);
5711dca9bdeSAnson Huang regulator_disable(data->vdd_reg);
57239631b5fSPeter Meerwald }
57339631b5fSPeter Meerwald
mag3110_suspend(struct device * dev)57439631b5fSPeter Meerwald static int mag3110_suspend(struct device *dev)
57539631b5fSPeter Meerwald {
5761dca9bdeSAnson Huang struct mag3110_data *data = iio_priv(i2c_get_clientdata(
5771dca9bdeSAnson Huang to_i2c_client(dev)));
5781dca9bdeSAnson Huang int ret;
5791dca9bdeSAnson Huang
5801dca9bdeSAnson Huang ret = mag3110_standby(iio_priv(i2c_get_clientdata(
58139631b5fSPeter Meerwald to_i2c_client(dev))));
5821dca9bdeSAnson Huang if (ret)
5831dca9bdeSAnson Huang return ret;
5841dca9bdeSAnson Huang
5851dca9bdeSAnson Huang ret = regulator_disable(data->vddio_reg);
5861dca9bdeSAnson Huang if (ret) {
5871dca9bdeSAnson Huang dev_err(dev, "failed to disable VDDIO regulator\n");
5881dca9bdeSAnson Huang return ret;
5891dca9bdeSAnson Huang }
5901dca9bdeSAnson Huang
5911dca9bdeSAnson Huang ret = regulator_disable(data->vdd_reg);
5921dca9bdeSAnson Huang if (ret) {
5931dca9bdeSAnson Huang dev_err(dev, "failed to disable VDD regulator\n");
5941dca9bdeSAnson Huang return ret;
5951dca9bdeSAnson Huang }
5961dca9bdeSAnson Huang
5971dca9bdeSAnson Huang return 0;
59839631b5fSPeter Meerwald }
59939631b5fSPeter Meerwald
mag3110_resume(struct device * dev)60039631b5fSPeter Meerwald static int mag3110_resume(struct device *dev)
60139631b5fSPeter Meerwald {
60239631b5fSPeter Meerwald struct mag3110_data *data = iio_priv(i2c_get_clientdata(
60339631b5fSPeter Meerwald to_i2c_client(dev)));
6041dca9bdeSAnson Huang int ret;
6051dca9bdeSAnson Huang
6061dca9bdeSAnson Huang ret = regulator_enable(data->vdd_reg);
6071dca9bdeSAnson Huang if (ret) {
6081dca9bdeSAnson Huang dev_err(dev, "failed to enable VDD regulator\n");
6091dca9bdeSAnson Huang return ret;
6101dca9bdeSAnson Huang }
6111dca9bdeSAnson Huang
6121dca9bdeSAnson Huang ret = regulator_enable(data->vddio_reg);
6131dca9bdeSAnson Huang if (ret) {
6141dca9bdeSAnson Huang dev_err(dev, "failed to enable VDDIO regulator\n");
6151dca9bdeSAnson Huang regulator_disable(data->vdd_reg);
6161dca9bdeSAnson Huang return ret;
6171dca9bdeSAnson Huang }
61839631b5fSPeter Meerwald
61939631b5fSPeter Meerwald return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
62039631b5fSPeter Meerwald data->ctrl_reg1);
62139631b5fSPeter Meerwald }
62239631b5fSPeter Meerwald
623fe7b9a38SJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(mag3110_pm_ops, mag3110_suspend,
624fe7b9a38SJonathan Cameron mag3110_resume);
62539631b5fSPeter Meerwald
62639631b5fSPeter Meerwald static const struct i2c_device_id mag3110_id[] = {
62739631b5fSPeter Meerwald { "mag3110", 0 },
62839631b5fSPeter Meerwald { }
62939631b5fSPeter Meerwald };
63039631b5fSPeter Meerwald MODULE_DEVICE_TABLE(i2c, mag3110_id);
63139631b5fSPeter Meerwald
632ee9a9073SJavier Martinez Canillas static const struct of_device_id mag3110_of_match[] = {
633ee9a9073SJavier Martinez Canillas { .compatible = "fsl,mag3110" },
634ee9a9073SJavier Martinez Canillas { }
635ee9a9073SJavier Martinez Canillas };
636ee9a9073SJavier Martinez Canillas MODULE_DEVICE_TABLE(of, mag3110_of_match);
637ee9a9073SJavier Martinez Canillas
63839631b5fSPeter Meerwald static struct i2c_driver mag3110_driver = {
63939631b5fSPeter Meerwald .driver = {
64039631b5fSPeter Meerwald .name = "mag3110",
641ee9a9073SJavier Martinez Canillas .of_match_table = mag3110_of_match,
642fe7b9a38SJonathan Cameron .pm = pm_sleep_ptr(&mag3110_pm_ops),
64339631b5fSPeter Meerwald },
644*7cf15f42SUwe Kleine-König .probe = mag3110_probe,
64539631b5fSPeter Meerwald .remove = mag3110_remove,
64639631b5fSPeter Meerwald .id_table = mag3110_id,
64739631b5fSPeter Meerwald };
64839631b5fSPeter Meerwald module_i2c_driver(mag3110_driver);
64939631b5fSPeter Meerwald
65039631b5fSPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
65139631b5fSPeter Meerwald MODULE_DESCRIPTION("Freescale MAG3110 magnetometer driver");
65239631b5fSPeter Meerwald MODULE_LICENSE("GPL");
653