Gsensor MMA8452Q传感器驱动及其datasheet

这个驱动是最近在搞Gsensor驱动的时候写的,主要是涉及到的是input子系统和hwmon设备系统,在编译时,需要进入内核选择input相关模块(直接选择“*”,不要选择“M”,否则需要手动insmod,增加不必要的时间消费)。否则,input提供给我们的接口我们岂不是浪费掉了,再者,对于我们这样的“懒人”,能借助现成的东西就不要费心费力的去开发,本文参考了部分MMA7660的驱动内容,本身就是一个系列的驱动相差也不会很大。

#include <linux/module.h>

#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/pm.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/hwmon-sysfs.h>
#include <linux/hwmon.h>
#include <linux/input-polldev.h>
#include <linux/input.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>


#define POLL_INTERVAL_MIN    1
#define POLL_INTERVAL_MAX    500
#define POLL_INTERVAL        100 /* msecs */
#define INPUT_FUZZ        32
#define INPUT_FLAT        32
#define MODE_CHANGE_DELAY_MS    100


#define MMA8452_STATUS_ZYXDR    0x08
#define MMA8452_BUF_SIZE    6

#define MMA8452_DEVICE_ID    0x2a

#define MMA8452_NAME        "mma8452"    


/* mma8452 registers */
enum mma8452_registers {
    MMA8452_STATUS = 0x00,
    MMA8452_OUT_X_MSB,
    MMA8452_OUT_X_LSB,
    MMA8452_OUT_Y_MSB,
    MMA8452_OUT_Y_LSB,
    MMA8452_OUT_Z_MSB,
    MMA8452_OUT_Z_LSB,


    MMA8452_SYSMOD = 0x0B,
    MMA8452_INT_SOURCE,
    MMA8452_WHO_AM_I,
    MMA8452_XYZ_DATA_CFG,
    MMA8452_HP_FILTER_CUTOFF,
    MMA8452_PL_STATUS,
    MMA8452_PL_CFG,
    MMA8452_PL_COUNT,
    MMA8452_PL_BF_ZCOMP,
    MMA8452_P_L_THS_REG,
    MMA8452_FF_MT_CFG,
    MMA8452_FF_MT_SRC,
    MMA8452_FF_MT_THS,
    MMA8452_FF_MT_COUNT,


    MMA8452_TRANSIENT_CFG = 0x1D,
    MMA8452_TRANSIENT_SRC,
    MMA8452_TRANSIENT_THS,
    MMA8452_TRANSIENT_COUNT,
    MMA8452_PULSE_CFG,
    MMA8452_PULSE_SRC,
    MMA8452_PULSE_THSX,
    MMA8452_PULSE_THSY,
    MMA8452_PULSE_THSZ,
    MMA8452_PULSE_TMLT,
    MMA8452_PULSE_LTCY,
    MMA8452_PULSE_WIND,
    MMA8452_ASLP_COUNT,
    MMA8452_CTRL_REG1,
    MMA8452_CTRL_REG2,
    MMA8452_CTRL_REG3,
    MMA8452_CTRL_REG4,
    MMA8452_CTRL_REG5,
    MMA8452_OFF_X,
    MMA8452_OFF_Y,
    MMA8452_OFF_Z,
    MMA8452_REG_END,
};




/* 

MMA8452 have 3 different mode, each mode have different sensitivity

 * as below */
/* MODE_2G: sensitivity is 1024 counts/g
 * MODE_4G: sensitivity is 512 counts/g

 * MODE_8G: sensitivity is 256 counts/g

 */


enum mma8452_mode {
    MODE_2G = 0,
    MODE_4G,
    MODE_8G,
};


struct mma8452_info {
    u8 mode;
    u8 ctl_reg;
    struct i2c_client *client;
    struct input_polled_dev *idev;
    struct device *hwmon_dev;
};


static DEFINE_MUTEX(mma8452_lock);


/* Default use 2G mode */
#define DEFAULT_SENSTIVE_MODE MODE_2G


static int mma8452_change_mode(struct mma8452_info *priv)
{
    int ret;
    priv->ctl_reg |= 0x01;
    ret = i2c_smbus_write_byte_data(priv->client, MMA8452_CTRL_REG1, 0);
    if (ret < 0)
    return ret;
    ret = i2c_smbus_write_byte_data(priv->client,
                    MMA8452_XYZ_DATA_CFG, priv->mode);
    ret |= i2c_smbus_write_byte_data(priv->client,
                     MMA8452_CTRL_REG1, priv->ctl_reg);
    if (ret < 0) {
        dev_err(&priv->client->dev, "mma8452 init error");
   
return ret;       
    }


    mdelay(MODE_CHANGE_DELAY_MS);
    return 0;

}


static int init_mma8452_chip(struct mma8452_info *priv)
{
    return mma8452_change_mode(priv);
}


static int mma8452_read_data(struct mma8452_info *priv, short *x,
             short *y, short *z)
{
    u8 buf[MMA8452_BUF_SIZE];
    int ret;


    ret = i2c_smbus_read_i2c_block_data(priv->client, MMA8452_OUT_X_MSB,
                     MMA8452_BUF_SIZE, buf);
    if (ret < MMA8452_BUF_SIZE) {
        dev_err(&priv->client->dev, "i2c block read failed\n");
        return -EIO;
    }


    *x = (buf[0] << 8) | buf[1];
    *y = (buf[2] << 8) | buf[3];
    *z = (buf[4] << 8) | buf[5];
    *x >>= 2;
    *y >>= 2;
    *z >>= 2;


    if (priv->mode == MODE_4G) {
        *x <<= 1;
        *y <<= 1;
        *z <<= 1;
    } else if (priv->mode == MODE_8G) {
        *x <<= 2;
        *y <<= 2;
        *z <<= 2;
    }


    return 0;
}


static void mma8452_dev_poll(struct input_polled_dev *dev)
{
    struct mma8452_info *priv = dev->private;


    short x = -1, y = -1, z = -1;
    int ret;


    mutex_lock(&mma8452_lock);


    do
        ret = i2c_smbus_read_byte_data(priv->client, MMA8452_STATUS);
    while (!(ret & MMA8452_STATUS_ZYXDR));


    ret = mma8452_read_data(priv, &x, &y, &z);
    if (!ret) {
        input_report_abs(priv->idev->input, ABS_X, x);
        input_report_abs(priv->idev->input, ABS_Y, y);
        input_report_abs(priv->idev->input, ABS_Z, z);
        input_sync(priv->idev->input);
    }


    mutex_unlock(&mma8452_lock);
}


/*
 * detecting whether mma8452 is on board.
 */
static int is_mma8452_device_id(struct i2c_client *client)
{
    int ret;


    ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I);


    if (ret == MMA8452_DEVICE_ID)
        return 0;
    else
        return -EINVAL;
}


static int  mma8452_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
    int ret;
    struct input_dev *input_idev;
    struct i2c_adapter *adapter;
    struct mma8452_info *priv;


  adapter = to_i2c_adapter(client->dev.parent);
    ret = i2c_check_functionality(adapter,I2C_FUNC_I2C|I2C_FUNC_SMBUS_BYTE_DATA);
    if (!ret)
        goto err_out;


    priv = kzalloc(sizeof(struct mma8452_info), GFP_KERNEL);
    if (!priv) {
        dev_err(&client->dev, "failed to alloc driver info\n");
        goto err_out;
    }


    ret = is_mma8452_device_id(client);
    if(!ret)
        goto err_out;


    priv->client = client;
    priv->mode = DEFAULT_SENSTIVE_MODE;


    dev_dbg(&client->dev, "found %s model accelerator\n",
         MMA8452_NAME);


    ret = init_mma8452_chip(priv);


    if (ret) {
        dev_err(&client->dev,
            "error when init %s chip:(%d)\n",
            MMA8452_NAME, ret);
        goto err_alloc_priv;
    }


    priv->hwmon_dev = hwmon_device_register(&client->dev);
    if (!priv->hwmon_dev) {
        ret = -ENOMEM;
        dev_err(&client->dev,
            "error register hwmon device\n");
        goto err_alloc_priv;
    }


    priv->idev = input_allocate_polled_device();
    if (!priv->idev) {
        ret = -ENOMEM;
        dev_err(&client->dev, "alloc poll device failed!\n");
        goto err_alloc_poll_device;
    }
    priv->idev->private = priv;
    priv->idev->poll = mma8452_dev_poll;
    priv->idev->poll_interval = POLL_INTERVAL;
    priv->idev->poll_interval_min = POLL_INTERVAL_MIN;
    priv->idev->poll_interval_max = POLL_INTERVAL_MAX;


    input_idev = priv->idev->input;
    input_idev->name = MMA8452_NAME;
    input_idev->id.bustype = BUS_I2C;
    input_idev->evbit[0] = BIT_MASK(EV_ABS);


    input_set_abs_params(input_idev, ABS_X, -8192, 8191,
             INPUT_FUZZ, INPUT_FLAT);
    input_set_abs_params(input_idev, ABS_Y, -8192, 8191,
             INPUT_FUZZ, INPUT_FLAT);
    input_set_abs_params(input_idev, ABS_Z, -8192, 8191,
             INPUT_FUZZ, INPUT_FLAT);


    ret = input_register_polled_device(priv->idev);
    if (ret) {
        dev_err(&client->dev, "register poll device failed!\n");
        goto err_register_polled_device;
    }


    i2c_set_clientdata(client, priv);
    dev_dbg(&client->dev, "%s accelerator init success\n",
        MMA8452_NAME);


    return 0;
err_register_polled_device:
    input_free_polled_device(priv->idev);
err_alloc_poll_device:
    hwmon_device_unregister(&client->dev);
err_alloc_priv:
    kfree(priv);
err_out:
    return ret;
}


static int mma8452_stop_chip(struct i2c_client *client)
{
    struct mma8452_info *priv = i2c_get_clientdata(client);
    int ret;


    priv->ctl_reg = i2c_smbus_read_byte_data(client,
                             MMA8452_CTRL_REG1);
    ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1,
                        priv->ctl_reg & 0xFE);
    return ret;
}


static int  mma8452_remove(struct i2c_client *client)
{
    int ret;
    struct mma8452_info *priv = i2c_get_clientdata(client);
    ret = mma8452_stop_chip(client);
    input_free_polled_device(priv->idev);
    hwmon_device_unregister(priv->hwmon_dev);

    return ret;
}


#ifdef CONFIG_PM_SLEEP
static int mma8452_suspend(struct device *dev)
{
    struct i2c_client *client = to_i2c_client(dev);


    return mma8452_stop_chip(client);
}


static int mma8452_resume(struct device *dev)
{
    struct i2c_client *client = to_i2c_client(dev);
    struct mma8452_info *priv = i2c_get_clientdata(client);


    return init_mma8452_chip(priv);
}
#endif


static const struct i2c_device_id mma8452_id[] = {
    {"mma8452", 0},
    {},
};
MODULE_DEVICE_TABLE(i2c, mma8452_id);


static SIMPLE_DEV_PM_OPS(mma8452_pm_ops, mma8452_suspend, mma8452_resume);
static struct i2c_driver mma8452_driver = {
    .driver = {
         .name = "mma8452",
         .owner = THIS_MODULE,
         .pm = &mma8452_pm_ops,
         },
    .probe= mma8452_probe,
    .remove = mma8452_remove,
    .id_table = mma8452_id
};


static int __init mma8452_init(void)
{
    return i2c_add_driver(&mma8452_driver);
}


static void __exit mma8452_exit(void)
{
    i2c_del_driver(&mma8452_driver);
}


module_init(mma8452_init);
module_exit(mma8452_exit);
MODULE_AUTHOR("Andy wangmutian ");
MODULE_DESCRIPTION("Freescale MMA8452  sensors");
MODULE_LICENSE("GPL ");
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值