linux/drivers/iio/magnetometer/mag3110.c
<<
>>
Prefs
   1/*
   2 * mag3110.c - Support for Freescale MAG3110 magnetometer sensor
   3 *
   4 * Copyright (c) 2013 Peter Meerwald <pmeerw@pmeerw.net>
   5 *
   6 * This file is subject to the terms and conditions of version 2 of
   7 * the GNU General Public License.  See the file COPYING in the main
   8 * directory of this archive for more details.
   9 *
  10 * (7-bit I2C slave address 0x0e)
  11 *
  12 * TODO: irq, user offset, oversampling, continuous mode
  13 */
  14
  15#include <linux/module.h>
  16#include <linux/i2c.h>
  17#include <linux/iio/iio.h>
  18#include <linux/iio/sysfs.h>
  19#include <linux/iio/trigger_consumer.h>
  20#include <linux/iio/buffer.h>
  21#include <linux/iio/triggered_buffer.h>
  22#include <linux/delay.h>
  23
  24#define MAG3110_STATUS 0x00
  25#define MAG3110_OUT_X 0x01 /* MSB first */
  26#define MAG3110_OUT_Y 0x03
  27#define MAG3110_OUT_Z 0x05
  28#define MAG3110_WHO_AM_I 0x07
  29#define MAG3110_OFF_X 0x09 /* MSB first */
  30#define MAG3110_OFF_Y 0x0b
  31#define MAG3110_OFF_Z 0x0d
  32#define MAG3110_DIE_TEMP 0x0f
  33#define MAG3110_CTRL_REG1 0x10
  34#define MAG3110_CTRL_REG2 0x11
  35
  36#define MAG3110_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
  37
  38#define MAG3110_CTRL_DR_MASK (BIT(7) | BIT(6) | BIT(5))
  39#define MAG3110_CTRL_DR_SHIFT 5
  40#define MAG3110_CTRL_DR_DEFAULT 0
  41
  42#define MAG3110_CTRL_TM BIT(1) /* trigger single measurement */
  43#define MAG3110_CTRL_AC BIT(0) /* continuous measurements */
  44
  45#define MAG3110_CTRL_AUTO_MRST_EN BIT(7) /* magnetic auto-reset */
  46#define MAG3110_CTRL_RAW BIT(5) /* measurements not user-offset corrected */
  47
  48#define MAG3110_DEVICE_ID 0xc4
  49
  50/* Each client has this additional data */
  51struct mag3110_data {
  52        struct i2c_client *client;
  53        struct mutex lock;
  54        u8 ctrl_reg1;
  55};
  56
  57static int mag3110_request(struct mag3110_data *data)
  58{
  59        int ret, tries = 150;
  60
  61        /* trigger measurement */
  62        ret = i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
  63                data->ctrl_reg1 | MAG3110_CTRL_TM);
  64        if (ret < 0)
  65                return ret;
  66
  67        while (tries-- > 0) {
  68                ret = i2c_smbus_read_byte_data(data->client, MAG3110_STATUS);
  69                if (ret < 0)
  70                        return ret;
  71                /* wait for data ready */
  72                if ((ret & MAG3110_STATUS_DRDY) == MAG3110_STATUS_DRDY)
  73                        break;
  74                msleep(20);
  75        }
  76
  77        if (tries < 0) {
  78                dev_err(&data->client->dev, "data not ready\n");
  79                return -EIO;
  80        }
  81
  82        return 0;
  83}
  84
  85static int mag3110_read(struct mag3110_data *data, __be16 buf[3])
  86{
  87        int ret;
  88
  89        mutex_lock(&data->lock);
  90        ret = mag3110_request(data);
  91        if (ret < 0) {
  92                mutex_unlock(&data->lock);
  93                return ret;
  94        }
  95        ret = i2c_smbus_read_i2c_block_data(data->client,
  96                MAG3110_OUT_X, 3 * sizeof(__be16), (u8 *) buf);
  97        mutex_unlock(&data->lock);
  98
  99        return ret;
 100}
 101
 102static ssize_t mag3110_show_int_plus_micros(char *buf,
 103        const int (*vals)[2], int n)
 104{
 105        size_t len = 0;
 106
 107        while (n-- > 0)
 108                len += scnprintf(buf + len, PAGE_SIZE - len,
 109                        "%d.%06d ", vals[n][0], vals[n][1]);
 110
 111        /* replace trailing space by newline */
 112        buf[len - 1] = '\n';
 113
 114        return len;
 115}
 116
 117static int mag3110_get_int_plus_micros_index(const int (*vals)[2], int n,
 118                                        int val, int val2)
 119{
 120        while (n-- > 0)
 121                if (val == vals[n][0] && val2 == vals[n][1])
 122                        return n;
 123
 124        return -EINVAL;
 125}
 126
 127static const int mag3110_samp_freq[8][2] = {
 128        {80, 0}, {40, 0}, {20, 0}, {10, 0}, {5, 0}, {2, 500000},
 129        {1, 250000}, {0, 625000}
 130};
 131
 132static ssize_t mag3110_show_samp_freq_avail(struct device *dev,
 133                                struct device_attribute *attr, char *buf)
 134{
 135        return mag3110_show_int_plus_micros(buf, mag3110_samp_freq, 8);
 136}
 137
 138static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mag3110_show_samp_freq_avail);
 139
 140static int mag3110_get_samp_freq_index(struct mag3110_data *data,
 141        int val, int val2)
 142{
 143        return mag3110_get_int_plus_micros_index(mag3110_samp_freq, 8, val,
 144                val2);
 145}
 146
 147static int mag3110_read_raw(struct iio_dev *indio_dev,
 148                            struct iio_chan_spec const *chan,
 149                            int *val, int *val2, long mask)
 150{
 151        struct mag3110_data *data = iio_priv(indio_dev);
 152        __be16 buffer[3];
 153        int i, ret;
 154
 155        switch (mask) {
 156        case IIO_CHAN_INFO_RAW:
 157                ret = iio_device_claim_direct_mode(indio_dev);
 158                if (ret)
 159                        return ret;
 160
 161                switch (chan->type) {
 162                case IIO_MAGN: /* in 0.1 uT / LSB */
 163                        ret = mag3110_read(data, buffer);
 164                        if (ret < 0)
 165                                goto release;
 166                        *val = sign_extend32(
 167                                be16_to_cpu(buffer[chan->scan_index]), 15);
 168                        ret = IIO_VAL_INT;
 169                        break;
 170                case IIO_TEMP: /* in 1 C / LSB */
 171                        mutex_lock(&data->lock);
 172                        ret = mag3110_request(data);
 173                        if (ret < 0) {
 174                                mutex_unlock(&data->lock);
 175                                goto release;
 176                        }
 177                        ret = i2c_smbus_read_byte_data(data->client,
 178                                MAG3110_DIE_TEMP);
 179                        mutex_unlock(&data->lock);
 180                        if (ret < 0)
 181                                goto release;
 182                        *val = sign_extend32(ret, 7);
 183                        ret = IIO_VAL_INT;
 184                        break;
 185                default:
 186                        ret = -EINVAL;
 187                }
 188release:
 189                iio_device_release_direct_mode(indio_dev);
 190                return ret;
 191
 192        case IIO_CHAN_INFO_SCALE:
 193                switch (chan->type) {
 194                case IIO_MAGN:
 195                        *val = 0;
 196                        *val2 = 1000;
 197                        return IIO_VAL_INT_PLUS_MICRO;
 198                case IIO_TEMP:
 199                        *val = 1000;
 200                        return IIO_VAL_INT;
 201                default:
 202                        return -EINVAL;
 203                }
 204        case IIO_CHAN_INFO_SAMP_FREQ:
 205                i = data->ctrl_reg1 >> MAG3110_CTRL_DR_SHIFT;
 206                *val = mag3110_samp_freq[i][0];
 207                *val2 = mag3110_samp_freq[i][1];
 208                return IIO_VAL_INT_PLUS_MICRO;
 209        case IIO_CHAN_INFO_CALIBBIAS:
 210                ret = i2c_smbus_read_word_swapped(data->client,
 211                        MAG3110_OFF_X + 2 * chan->scan_index);
 212                if (ret < 0)
 213                        return ret;
 214                *val = sign_extend32(ret >> 1, 14);
 215                return IIO_VAL_INT;
 216        }
 217        return -EINVAL;
 218}
 219
 220static int mag3110_write_raw(struct iio_dev *indio_dev,
 221                             struct iio_chan_spec const *chan,
 222                             int val, int val2, long mask)
 223{
 224        struct mag3110_data *data = iio_priv(indio_dev);
 225        int rate, ret;
 226
 227        ret = iio_device_claim_direct_mode(indio_dev);
 228        if (ret)
 229                return ret;
 230
 231        switch (mask) {
 232        case IIO_CHAN_INFO_SAMP_FREQ:
 233                rate = mag3110_get_samp_freq_index(data, val, val2);
 234                if (rate < 0) {
 235                        ret = -EINVAL;
 236                        break;
 237                }
 238
 239                data->ctrl_reg1 &= ~MAG3110_CTRL_DR_MASK;
 240                data->ctrl_reg1 |= rate << MAG3110_CTRL_DR_SHIFT;
 241                ret = i2c_smbus_write_byte_data(data->client,
 242                        MAG3110_CTRL_REG1, data->ctrl_reg1);
 243                break;
 244        case IIO_CHAN_INFO_CALIBBIAS:
 245                if (val < -10000 || val > 10000) {
 246                        ret = -EINVAL;
 247                        break;
 248                }
 249                ret = i2c_smbus_write_word_swapped(data->client,
 250                        MAG3110_OFF_X + 2 * chan->scan_index, val << 1);
 251                break;
 252        default:
 253                ret = -EINVAL;
 254                break;
 255        }
 256        iio_device_release_direct_mode(indio_dev);
 257        return ret;
 258}
 259
 260static irqreturn_t mag3110_trigger_handler(int irq, void *p)
 261{
 262        struct iio_poll_func *pf = p;
 263        struct iio_dev *indio_dev = pf->indio_dev;
 264        struct mag3110_data *data = iio_priv(indio_dev);
 265        u8 buffer[16]; /* 3 16-bit channels + 1 byte temp + padding + ts */
 266        int ret;
 267
 268        ret = mag3110_read(data, (__be16 *) buffer);
 269        if (ret < 0)
 270                goto done;
 271
 272        if (test_bit(3, indio_dev->active_scan_mask)) {
 273                ret = i2c_smbus_read_byte_data(data->client,
 274                        MAG3110_DIE_TEMP);
 275                if (ret < 0)
 276                        goto done;
 277                buffer[6] = ret;
 278        }
 279
 280        iio_push_to_buffers_with_timestamp(indio_dev, buffer,
 281                iio_get_time_ns(indio_dev));
 282
 283done:
 284        iio_trigger_notify_done(indio_dev->trig);
 285        return IRQ_HANDLED;
 286}
 287
 288#define MAG3110_CHANNEL(axis, idx) { \
 289        .type = IIO_MAGN, \
 290        .modified = 1, \
 291        .channel2 = IIO_MOD_##axis, \
 292        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
 293                BIT(IIO_CHAN_INFO_CALIBBIAS), \
 294        .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
 295                BIT(IIO_CHAN_INFO_SCALE), \
 296        .scan_index = idx, \
 297        .scan_type = { \
 298                .sign = 's', \
 299                .realbits = 16, \
 300                .storagebits = 16, \
 301                .endianness = IIO_BE, \
 302        }, \
 303}
 304
 305static const struct iio_chan_spec mag3110_channels[] = {
 306        MAG3110_CHANNEL(X, 0),
 307        MAG3110_CHANNEL(Y, 1),
 308        MAG3110_CHANNEL(Z, 2),
 309        {
 310                .type = IIO_TEMP,
 311                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 312                        BIT(IIO_CHAN_INFO_SCALE),
 313                .scan_index = 3,
 314                .scan_type = {
 315                        .sign = 's',
 316                        .realbits = 8,
 317                        .storagebits = 8,
 318                        },
 319        },
 320        IIO_CHAN_SOFT_TIMESTAMP(4),
 321};
 322
 323static struct attribute *mag3110_attributes[] = {
 324        &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
 325        NULL
 326};
 327
 328static const struct attribute_group mag3110_group = {
 329        .attrs = mag3110_attributes,
 330};
 331
 332static const struct iio_info mag3110_info = {
 333        .attrs = &mag3110_group,
 334        .read_raw = &mag3110_read_raw,
 335        .write_raw = &mag3110_write_raw,
 336        .driver_module = THIS_MODULE,
 337};
 338
 339static const unsigned long mag3110_scan_masks[] = {0x7, 0xf, 0};
 340
 341static int mag3110_standby(struct mag3110_data *data)
 342{
 343        return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
 344                data->ctrl_reg1 & ~MAG3110_CTRL_AC);
 345}
 346
 347static int mag3110_probe(struct i2c_client *client,
 348                         const struct i2c_device_id *id)
 349{
 350        struct mag3110_data *data;
 351        struct iio_dev *indio_dev;
 352        int ret;
 353
 354        ret = i2c_smbus_read_byte_data(client, MAG3110_WHO_AM_I);
 355        if (ret < 0)
 356                return ret;
 357        if (ret != MAG3110_DEVICE_ID)
 358                return -ENODEV;
 359
 360        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
 361        if (!indio_dev)
 362                return -ENOMEM;
 363
 364        data = iio_priv(indio_dev);
 365        data->client = client;
 366        mutex_init(&data->lock);
 367
 368        i2c_set_clientdata(client, indio_dev);
 369        indio_dev->info = &mag3110_info;
 370        indio_dev->name = id->name;
 371        indio_dev->dev.parent = &client->dev;
 372        indio_dev->modes = INDIO_DIRECT_MODE;
 373        indio_dev->channels = mag3110_channels;
 374        indio_dev->num_channels = ARRAY_SIZE(mag3110_channels);
 375        indio_dev->available_scan_masks = mag3110_scan_masks;
 376
 377        data->ctrl_reg1 = MAG3110_CTRL_DR_DEFAULT << MAG3110_CTRL_DR_SHIFT;
 378        ret = i2c_smbus_write_byte_data(client, MAG3110_CTRL_REG1,
 379                data->ctrl_reg1);
 380        if (ret < 0)
 381                return ret;
 382
 383        ret = i2c_smbus_write_byte_data(client, MAG3110_CTRL_REG2,
 384                MAG3110_CTRL_AUTO_MRST_EN);
 385        if (ret < 0)
 386                goto standby_on_error;
 387
 388        ret = iio_triggered_buffer_setup(indio_dev, NULL,
 389                mag3110_trigger_handler, NULL);
 390        if (ret < 0)
 391                goto standby_on_error;
 392
 393        ret = iio_device_register(indio_dev);
 394        if (ret < 0)
 395                goto buffer_cleanup;
 396        return 0;
 397
 398buffer_cleanup:
 399        iio_triggered_buffer_cleanup(indio_dev);
 400standby_on_error:
 401        mag3110_standby(iio_priv(indio_dev));
 402        return ret;
 403}
 404
 405static int mag3110_remove(struct i2c_client *client)
 406{
 407        struct iio_dev *indio_dev = i2c_get_clientdata(client);
 408
 409        iio_device_unregister(indio_dev);
 410        iio_triggered_buffer_cleanup(indio_dev);
 411        mag3110_standby(iio_priv(indio_dev));
 412
 413        return 0;
 414}
 415
 416#ifdef CONFIG_PM_SLEEP
 417static int mag3110_suspend(struct device *dev)
 418{
 419        return mag3110_standby(iio_priv(i2c_get_clientdata(
 420                to_i2c_client(dev))));
 421}
 422
 423static int mag3110_resume(struct device *dev)
 424{
 425        struct mag3110_data *data = iio_priv(i2c_get_clientdata(
 426                to_i2c_client(dev)));
 427
 428        return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
 429                data->ctrl_reg1);
 430}
 431
 432static SIMPLE_DEV_PM_OPS(mag3110_pm_ops, mag3110_suspend, mag3110_resume);
 433#define MAG3110_PM_OPS (&mag3110_pm_ops)
 434#else
 435#define MAG3110_PM_OPS NULL
 436#endif
 437
 438static const struct i2c_device_id mag3110_id[] = {
 439        { "mag3110", 0 },
 440        { }
 441};
 442MODULE_DEVICE_TABLE(i2c, mag3110_id);
 443
 444static const struct of_device_id mag3110_of_match[] = {
 445        { .compatible = "fsl,mag3110" },
 446        { }
 447};
 448MODULE_DEVICE_TABLE(of, mag3110_of_match);
 449
 450static struct i2c_driver mag3110_driver = {
 451        .driver = {
 452                .name   = "mag3110",
 453                .of_match_table = mag3110_of_match,
 454                .pm     = MAG3110_PM_OPS,
 455        },
 456        .probe = mag3110_probe,
 457        .remove = mag3110_remove,
 458        .id_table = mag3110_id,
 459};
 460module_i2c_driver(mag3110_driver);
 461
 462MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
 463MODULE_DESCRIPTION("Freescale MAG3110 magnetometer driver");
 464MODULE_LICENSE("GPL");
 465