tiny4412 linux-4.2 移植(六)加速度传感器mma7660

简介

mm7660跟exynos 4412通过i2c进行通信,它所占用的引脚有:中断引脚XEINT25、i2c数据线i2cSDA3_OUT和i2c时钟线i2cSCL3_OUT。上上节我们已经了解了samsung的i2c控制器,现在我们需要在dts中配置i2c相关信息,然后移植mma7660驱动到linux-4.20。
在这里插入图片描述
在这里插入图片描述

dts

mma7660使用的是i2c3,所以这里需要配置i2c3。

&i2c_3 {
    samsung,i2c-sda-delay = <100>;    //esynos4412所有dts中的delay都是100,所以这个选择100
    samsung,i2c-max-bus-freq = <200000>;    //最高400 kbit/s,当然你可以填100000 Supports up to 400 kbit/s in the Fast mode.
    status = "okay";
    mma7660: mma7660@4c {
        compatible = "tiny4412,mma7660";
        reg = <0x4c>;			//设备地址
        interrupt-parent = <&gpx3>;
        interrupts = <1 IRQ_TYPE_EDGE_FALLING>;    /* 选中的是GPX3_1  */
        input_fuzz = <4>;
        input_flat = <4>;
        poll_interval = <100>;
        status = "okay";
    };
};

mma7660.c

mma7660的移植参考友善之臂官方的驱动程序就行了,下面是我移植后的程序。

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
old mode 100644
new mode 100755
index ca59a2b..79b52da
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -12,6 +12,12 @@ menuconfig INPUT_MISC
if INPUT_MISC
+config INPUT_MMA7660
+       tristate "Tiny4412 Accelerometer mma7660"
+       help
+               Say Y here to support Accelerometer mma7660 for tiny4412.
+               If unsure, say N.
+
config INPUT_88PM860X_ONKEY
        tristate "88PM860x ONKEY support"
        depends on MFD_88PM860X
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
old mode 100644
new mode 100755
index 9d0f9d1..2aa2600
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -5,6 +5,7 @@
# Each configuration option enables a list of files.
+obj-$(CONFIG_INPUT_MMA7660)         += mma7660.o
obj-$(CONFIG_INPUT_88PM860X_ONKEY)     += 88pm860x_onkey.o
obj-$(CONFIG_INPUT_88PM80X_ONKEY)      += 88pm80x_onkey.o
obj-$(CONFIG_INPUT_AB8500_PONKEY)      += ab8500-ponkey.o
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/input-polldev.h>
#include <linux/of_device.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/log2.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/property.h>
#include <linux/acpi.h>
#include <linux/regmap.h>
#include <linux/pm_runtime.h>
#include <linux/gpio/consumer.h>
#include <linux/err.h>
/* MMA7760 Registers */
#define MMA7660_XOUT            0x00    // 6-bit output value X
#define MMA7660_YOUT            0x01    // 6-bit output value Y
#define MMA7660_ZOUT            0x02    // 6-bit output value Z
#define MMA7660_TILT            0x03    // Tilt status
#define MMA7660_SRST            0x04    // Sampling Rate Status
#define MMA7660_SPCNT            0x05    // Sleep Count
#define MMA7660_INTSU            0x06    // Interrupt Setup
#define MMA7660_MODE            0x07    // Mode
#define MMA7660_SR                0x08    // Auto-Wake/Sleep and Debounce Filter
#define MMA7660_PDET            0x09    // Tap Detection
#define MMA7660_PD                0x0a    // Tap Debounce Count
static const char *mma7660_bafro[] = {
    "Unknown", "Front", "Back"
};
static const char *mma7660_pola[] = {
    "Unknown",
    "Left", "Right",
    "Rsvd", "Rsvd",
    "Down", "Up",
    "Rsvd",
};
#define MMA7660_NAME    "mma7660"
#define INPUT_FUZZ            4    //default vale
#define INPUT_FLAT            4    //default vale
#define POLL_INTERVAL        100    //default vale
#define __need_retry(__v)    (__v & (1 << 6))
#define __is_negative(__v)    (__v & (1 << 5))
static int                    last_tilt = 0;
static struct device        *hwmon_dev;
struct regmap *g_regmap = NULL;
static struct input_polled_dev    *mma7660_idev;
static struct i2c_client    *mma7660_client;
static const struct i2c_device_id mma7660_id[] = {
    { MMA7660_NAME, 0 },
    { },
};
MODULE_DEVICE_TABLE(i2c, mma7660_id);


static const struct of_device_id mma7660_dt_ids[] = {
    { .compatible = "tiny4412,mma7660", },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mma7660_dt_ids);
static int mma7660_read_xyz(struct i2c_client *client, int idx, int *xyz)
{
    int val;
    do {
        val = i2c_smbus_read_byte_data(client, idx + MMA7660_XOUT);
        if (val < 0) {
            dev_err(&client->dev, "Read register %02x failed, %d\n",
                    idx + MMA7660_XOUT, val);
            return -EIO;
        }
    } while (__need_retry(val));
    *xyz = __is_negative(val) ? (val | ~0x3f) : (val & 0x3f);
    return 0;
}
static int mma7660_read_tilt(struct i2c_client *client, int *tilt)
{
    int val;
    do {
        val = i2c_smbus_read_byte_data(client, MMA7660_TILT);
        if (val < 0) {
            dev_err(&client->dev, "Read register %02x failed, %d\n",
                    MMA7660_TILT, val);
            return -EIO;
        }
    } while (__need_retry(val));
    *tilt = (val & 0xff);
    return 0;
}
static int mma7660_initialize(struct i2c_client *client)
{
    int val;
    /* Using test mode to probe chip */
    i2c_smbus_write_byte_data(client, MMA7660_MODE, 0x00);
    mdelay(10);
    i2c_smbus_write_byte_data(client, MMA7660_MODE, 0x04);
    mdelay(10);
    i2c_smbus_write_byte_data(client, MMA7660_XOUT, 0x3f);
    i2c_smbus_write_byte_data(client, MMA7660_YOUT, 0x01);
    i2c_smbus_write_byte_data(client, MMA7660_ZOUT, 0x15);
    val = i2c_smbus_read_byte_data(client, MMA7660_ZOUT);
    if (val != 0x15) {
        dev_err(&client->dev, "no device\n");
        return -ENODEV;
    }
    /* Goto standby mode for configuration */
    i2c_smbus_write_byte_data(client, MMA7660_MODE, 0x00);
    mdelay(10);
    /* Sample rate: 64Hz / 16Hz; Filt: 3 samples  */
    i2c_smbus_write_byte_data(client, MMA7660_SR, ((2<<5) | (1<<3) | 1));
    /* Sleep count */
    i2c_smbus_write_byte_data(client, MMA7660_SPCNT, 0xA0);
    /* Tap detect and debounce ~4ms */
    i2c_smbus_write_byte_data(client, MMA7660_PDET, 4);
    i2c_smbus_write_byte_data(client, MMA7660_PD, 15);
    /* Enable interrupt except exiting Auto-Sleep */
    i2c_smbus_write_byte_data(client, MMA7660_INTSU, 0xe7);
    /* IPP, Auto-wake, auto-sleep and standby */
    i2c_smbus_write_byte_data(client, MMA7660_MODE, 0x59);
    mdelay(10);
    /* Save current tilt status */
    mma7660_read_tilt(client, &last_tilt);
    mma7660_client = client;
    return 0;
}
/*-----------------------------------------------------------------------------
* sysfs group support
*/
static ssize_t mma7660_show_regs(struct device *dev,
        struct device_attribute *attr, char *buf)
{
    int reg, val;
    int i, len = 0;
    for (reg = 0; reg < 0x0b; reg++) {
        val = i2c_smbus_read_byte_data(mma7660_client, reg);
        len += sprintf(buf + len, "REG: 0x%02x = 0x%02x ...... [ ", reg, val);
        for (i = 7; i >= 0; i--) {
            len += sprintf(buf + len, "%d", (val >> i) & 1);
            if ((i % 4) == 0) {
                len += sprintf(buf + len, " ");
            }
        }
        len += sprintf(buf + len, "]\n");
    }
    return len;
}
static ssize_t mma7660_write_reg(struct device *dev,
        struct device_attribute *attr, const char *buf, size_t count)
{
    unsigned int reg, val;
    int ret;
    ret = sscanf(buf, "%x %x", &reg, &val);
    if (ret == 2) {
        if (reg >= 0 && reg <= 0x0a) {
            i2c_smbus_write_byte_data(mma7660_client, reg, val);
            val = i2c_smbus_read_byte_data(mma7660_client, reg);
            printk("REG: 0x%02x = 0x%02x\n", reg, val);
        }
    }
    return count;
}
static ssize_t mma7660_show_xyz_g(struct device *dev,
        struct device_attribute *attr, char *buf)
{
    int axis[3];
    int i;
    for (i = 0; i < 3; i++) {
        mma7660_read_xyz(mma7660_client, i, &axis[i]);
    }
    return sprintf(buf, "%3d, %3d, %3d\n", axis[0], axis[1], axis[2]);
}
static ssize_t mma7660_show_axis_g(struct device *dev,
        struct device_attribute *attr, char *buf)
{
    int n = to_sensor_dev_attr(attr)->index;
    int val;
    mma7660_read_xyz(mma7660_client, n, &val);
    return sprintf(buf, "%3d\n", val);
}
static ssize_t mma7660_show_tilt(struct device *dev,
        struct device_attribute *attr, char *buf)
{
    int val = 0, len = 0;
    mma7660_read_tilt(mma7660_client, &val);
    len += sprintf(buf + len, "%s", mma7660_bafro[val & 0x03]);
    len += sprintf(buf + len, ", %s", mma7660_pola[(val >> 2) & 0x07]);
    if (val & (1 << 5)) {
        len += sprintf(buf + len, ", Tap");
    }
    if (val & (1 << 7)) {
        len += sprintf(buf + len, ", Shake");
    }
    len += sprintf(buf + len, "\n");
    return len;
}
static SENSOR_DEVICE_ATTR(registers, 0660,mma7660_show_regs, mma7660_write_reg, 0);
static SENSOR_DEVICE_ATTR(x_axis_g, S_IRUGO, mma7660_show_axis_g, NULL, 0);
static SENSOR_DEVICE_ATTR(y_axis_g, S_IRUGO, mma7660_show_axis_g, NULL, 1);
static SENSOR_DEVICE_ATTR(z_axis_g, S_IRUGO, mma7660_show_axis_g, NULL, 2);
static SENSOR_DEVICE_ATTR(all_axis_g, S_IRUGO, mma7660_show_xyz_g, NULL, 0);
static SENSOR_DEVICE_ATTR(tilt_status, S_IRUGO, mma7660_show_tilt, NULL, 0);
static struct attribute* mma7660_attrs[] = {
    &sensor_dev_attr_registers.dev_attr.attr,
    &sensor_dev_attr_x_axis_g.dev_attr.attr,
    &sensor_dev_attr_y_axis_g.dev_attr.attr,
    &sensor_dev_attr_z_axis_g.dev_attr.attr,
    &sensor_dev_attr_all_axis_g.dev_attr.attr,
    &sensor_dev_attr_tilt_status.dev_attr.attr,
    NULL
};
static const struct attribute_group mma7660_group = {
    .attrs        = mma7660_attrs,
};
static void mma7660_worker(struct work_struct *work)
{
    int bafro, pola, shake, tap;
    int val = 0;
    mma7660_read_tilt(mma7660_client, &val);
    /* TODO: report it ? */
    bafro = val & 0x03;
    if (bafro != (last_tilt & 0x03)) {
        printk("%s\n", mma7660_bafro[bafro]);
    }
    pola = (val >> 2) & 0x07;
    if (pola != ((last_tilt >> 2) & 0x07)) {
        printk("%s\n", mma7660_pola[pola]);
    }
    shake = (val >> 5) & 0x01;
    if (shake && shake != ((last_tilt >> 5) & 0x01)) {
        printk("Tap\n");
    }
    tap = (val >> 7) & 0x01;
    if (tap && tap != ((last_tilt >> 7) & 0x01)) {
        printk("Shake\n");
    }
    /* Save current status */
    last_tilt = val;
}
DECLARE_WORK(mma7660_work, mma7660_worker);
static irqreturn_t mma7660_interrupt(int irq, void *data)
{
    printk("mma7660_interrupt\n");
    schedule_work(&mma7660_work);
    printk("mma7660_interrupt ok\n");
    return IRQ_HANDLED;
}
static void mma7660_report_abs(void)
{
    int axis[3];
    int i;
    for (i = 0; i < 3; i++) {
        mma7660_read_xyz(mma7660_client, i, &axis[i]);
    }
    input_report_abs(mma7660_idev->input, ABS_X, axis[0]);
    input_report_abs(mma7660_idev->input, ABS_Y, axis[1]);
    input_report_abs(mma7660_idev->input, ABS_Z, axis[2]);
    input_sync(mma7660_idev->input);


    //printk("3-Axis ... %3d, %3d, %3d\n", axis[0], axis[1], axis[2]);
}
static void mma7660_dev_poll(struct input_polled_dev *dev)
{
    mma7660_report_abs();
}
static int mma7760_probe(struct i2c_client *client,
             const struct i2c_device_id *id)
{
    struct device *dev = &client->dev;
    struct device_node *of_node = client->dev.of_node;
    struct regmap_config regmap_config = { };
    struct regmap *regmap;
    struct input_dev *idev;
    int input_fuzz = INPUT_FUZZ;
    int input_flat = INPUT_FLAT;
    int poll_interval = POLL_INTERVAL;
    int ret;
    u32 val;
    printk("enter %s\n", __func__);
    ret = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA);
    if (!ret) {
        dev_err(&client->dev, "I2C check functionality failed\n");
        return -ENXIO;
    }
    if (of_node && of_match_device(mma7660_dt_ids, dev)){
        ret = device_property_read_u32(dev, "input_fuzz", &val);
        if (!ret)
            input_fuzz = val;
        ret = device_property_read_u32(dev, "input_flat", &val);
        if (!ret)
            input_flat = val;
        ret = device_property_read_u32(dev, "poll_interval", &val);
        if (!ret)
            poll_interval = val;
    }
    regmap_config.val_bits = 8;
    regmap_config.reg_bits = 8;
    regmap_config.disable_locking = true;
    regmap = devm_regmap_init_i2c(client, &regmap_config); //这个可以去掉,当时为了比较regmap后的i2c是否可以兼容smbus,结果阅读
    													 //源码发现优先使用i2c的操作接口,除非控制器不支持i2c,不然是不会调用smbus
    													 //函数去通信
    if (IS_ERR(regmap))
        return PTR_ERR(regmap);
    if(mma7660_initialize(client)<0){
        goto error_init_client;
    }
    ret = sysfs_create_group(&client->dev.kobj, &mma7660_group);
    if (ret) {
        dev_err(&client->dev, "create sysfs group failed!\n");
        goto error_init_client;
    }
    hwmon_dev = hwmon_device_register(&client->dev);
    if (IS_ERR(hwmon_dev)) {
        dev_err(&client->dev, "hwmon register failed!\n");
        ret = PTR_ERR(hwmon_dev);
        goto error_rm_dev_file;
    }
    mma7660_idev = input_allocate_polled_device();
    if (!mma7660_idev) {
        dev_err(&client->dev, "alloc poll device failed!\n");
        ret = -ENOMEM;
        goto error_init_client;
    }
    mma7660_idev->poll = mma7660_dev_poll;
    mma7660_idev->poll_interval = poll_interval;
    idev = mma7660_idev->input;
    idev->name = MMA7660_NAME;
    idev->id.bustype = BUS_I2C;
    idev->id.vendor = 0x12FA;
    idev->id.product = 0x7660;
    idev->id.version = 0x0100;
    idev->dev.parent = &client->dev;
    set_bit(EV_ABS, idev->evbit);
    set_bit(ABS_X, idev->absbit);
    set_bit(ABS_Y, idev->absbit);
    set_bit(ABS_Z, idev->absbit);
    input_set_abs_params(idev, ABS_X, -512, 512, input_fuzz, input_flat);
    input_set_abs_params(idev, ABS_Y, -512, 512, input_fuzz, input_flat);
    input_set_abs_params(idev, ABS_Z, -512, 512, input_fuzz, input_flat);
    ret = input_register_polled_device(mma7660_idev);
    if (ret) {
        dev_err(&client->dev, "register poll device failed!\n");
        goto error_free_poll_dev;
    }
    printk(" client->irq:%d\n",client->irq);
    ret = devm_request_irq(dev, client->irq, mma7660_interrupt , IRQF_TRIGGER_FALLING, "mma7660", NULL);
    if (ret) {
        dev_err(&client->dev, "request irq (%d) failed %d\n", client->irq, ret);
        goto error_rm_poll_dev;
    }
    printk("exit %s  input_fuzz:%d input_flat:%d poll_interval:%d \n", __func__,input_fuzz,input_flat,poll_interval);
    ret = 0;
    return ret;
error_rm_poll_dev:
    input_unregister_polled_device(mma7660_idev);
error_free_poll_dev:
    input_free_polled_device(mma7660_idev);
error_rm_hwmon_dev:
    hwmon_device_unregister(hwmon_dev);
error_rm_dev_file:
    sysfs_remove_group(&client->dev.kobj, &mma7660_group);
error_init_client:
    return ret;
}
static struct i2c_driver mma7660_driver = {
    .driver = {
        .name    = MMA7660_NAME,
        .of_match_table = mma7660_dt_ids,
    },
    .probe        = mma7760_probe,
    .id_table    = mma7660_id,
};
module_i2c_driver(mma7660_driver);
MODULE_AUTHOR("xiaohuangji, Inc.");
MODULE_DESCRIPTION("tiny4412 Accelerometer");
MODULE_LICENSE("GPL");


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值