linux/drivers/misc/hmc6352.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * hmc6352.c - Honeywell Compass Driver
   4 *
   5 * Copyright (C) 2009 Intel Corp
   6 *
   7 *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   8 *
   9 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/slab.h>
  14#include <linux/i2c.h>
  15#include <linux/err.h>
  16#include <linux/delay.h>
  17#include <linux/sysfs.h>
  18#include <linux/nospec.h>
  19
  20static DEFINE_MUTEX(compass_mutex);
  21
  22static int compass_command(struct i2c_client *c, u8 cmd)
  23{
  24        int ret = i2c_master_send(c, &cmd, 1);
  25        if (ret < 0)
  26                dev_warn(&c->dev, "command '%c' failed.\n", cmd);
  27        return ret;
  28}
  29
  30static int compass_store(struct device *dev, const char *buf, size_t count,
  31                        const char *map)
  32{
  33        struct i2c_client *c = to_i2c_client(dev);
  34        int ret;
  35        unsigned long val;
  36
  37        ret = kstrtoul(buf, 10, &val);
  38        if (ret)
  39                return ret;
  40        if (val >= strlen(map))
  41                return -EINVAL;
  42        val = array_index_nospec(val, strlen(map));
  43        mutex_lock(&compass_mutex);
  44        ret = compass_command(c, map[val]);
  45        mutex_unlock(&compass_mutex);
  46        if (ret < 0)
  47                return ret;
  48        return count;
  49}
  50
  51static ssize_t compass_calibration_store(struct device *dev,
  52                struct device_attribute *attr, const char *buf, size_t count)
  53{
  54        return compass_store(dev, buf, count, "EC");
  55}
  56
  57static ssize_t compass_power_mode_store(struct device *dev,
  58                struct device_attribute *attr, const  char *buf, size_t count)
  59{
  60        return compass_store(dev, buf, count, "SW");
  61}
  62
  63static ssize_t compass_heading_data_show(struct device *dev,
  64                        struct device_attribute *attr, char *buf)
  65{
  66        struct i2c_client *client = to_i2c_client(dev);
  67        unsigned char i2c_data[2];
  68        int ret;
  69
  70        mutex_lock(&compass_mutex);
  71        ret = compass_command(client, 'A');
  72        if (ret != 1) {
  73                mutex_unlock(&compass_mutex);
  74                return ret;
  75        }
  76        msleep(10); /* sending 'A' cmd we need to wait for 7-10 millisecs */
  77        ret = i2c_master_recv(client, i2c_data, 2);
  78        mutex_unlock(&compass_mutex);
  79        if (ret < 0) {
  80                dev_warn(dev, "i2c read data cmd failed\n");
  81                return ret;
  82        }
  83        ret = (i2c_data[0] << 8) | i2c_data[1];
  84        return sprintf(buf, "%d.%d\n", ret/10, ret%10);
  85}
  86
  87
  88static DEVICE_ATTR(heading0_input, S_IRUGO, compass_heading_data_show, NULL);
  89static DEVICE_ATTR(calibration, S_IWUSR, NULL, compass_calibration_store);
  90static DEVICE_ATTR(power_state, S_IWUSR, NULL, compass_power_mode_store);
  91
  92static struct attribute *mid_att_compass[] = {
  93        &dev_attr_heading0_input.attr,
  94        &dev_attr_calibration.attr,
  95        &dev_attr_power_state.attr,
  96        NULL
  97};
  98
  99static const struct attribute_group m_compass_gr = {
 100        .name = "hmc6352",
 101        .attrs = mid_att_compass
 102};
 103
 104static int hmc6352_probe(struct i2c_client *client,
 105                                        const struct i2c_device_id *id)
 106{
 107        int res;
 108
 109        res = sysfs_create_group(&client->dev.kobj, &m_compass_gr);
 110        if (res) {
 111                dev_err(&client->dev, "device_create_file failed\n");
 112                return res;
 113        }
 114        dev_info(&client->dev, "%s HMC6352 compass chip found\n",
 115                                                        client->name);
 116        return 0;
 117}
 118
 119static int hmc6352_remove(struct i2c_client *client)
 120{
 121        sysfs_remove_group(&client->dev.kobj, &m_compass_gr);
 122        return 0;
 123}
 124
 125static const struct i2c_device_id hmc6352_id[] = {
 126        { "hmc6352", 0 },
 127        { }
 128};
 129
 130MODULE_DEVICE_TABLE(i2c, hmc6352_id);
 131
 132static struct i2c_driver hmc6352_driver = {
 133        .driver = {
 134                .name = "hmc6352",
 135        },
 136        .probe = hmc6352_probe,
 137        .remove = hmc6352_remove,
 138        .id_table = hmc6352_id,
 139};
 140
 141module_i2c_driver(hmc6352_driver);
 142
 143MODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com");
 144MODULE_DESCRIPTION("hmc6352 Compass Driver");
 145MODULE_LICENSE("GPL v2");
 146