| 1 | |
| 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 |
|
| 20 | static DEFINE_MUTEX(compass_mutex);
|
| 21 |
|
| 22 | static 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 |
|
| 30 | static 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 |
|
| 51 | static 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 |
|
| 57 | static 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 |
|
| 63 | static 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);
|
| 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 |
|
| 88 | static DEVICE_ATTR(heading0_input, S_IRUGO, compass_heading_data_show, NULL);
|
| 89 | static DEVICE_ATTR(calibration, S_IWUSR, NULL, compass_calibration_store);
|
| 90 | static DEVICE_ATTR(power_state, S_IWUSR, NULL, compass_power_mode_store);
|
| 91 |
|
| 92 | static 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 |
|
| 99 | static const struct attribute_group m_compass_gr = {
|
| 100 | .name = "hmc6352",
|
| 101 | .attrs = mid_att_compass
|
| 102 | };
|
| 103 |
|
| 104 | static int hmc6352_probe(struct i2c_client *client)
|
| 105 | {
|
| 106 | int res;
|
| 107 |
|
| 108 | res = sysfs_create_group(&client->dev.kobj, &m_compass_gr);
|
| 109 | if (res) {
|
| 110 | dev_err(&client->dev, "device_create_file failed\n");
|
| 111 | return res;
|
| 112 | }
|
| 113 | dev_info(&client->dev, "%s HMC6352 compass chip found\n",
|
| 114 | client->name);
|
| 115 | return 0;
|
| 116 | }
|
| 117 |
|
| 118 | static void hmc6352_remove(struct i2c_client *client)
|
| 119 | {
|
| 120 | sysfs_remove_group(&client->dev.kobj, &m_compass_gr);
|
| 121 | }
|
| 122 |
|
| 123 | static const struct i2c_device_id hmc6352_id[] = {
|
| 124 | { .name = "hmc6352" },
|
| 125 | { }
|
| 126 | };
|
| 127 |
|
| 128 | MODULE_DEVICE_TABLE(i2c, hmc6352_id);
|
| 129 |
|
| 130 | static struct i2c_driver hmc6352_driver = {
|
| 131 | .driver = {
|
| 132 | .name = "hmc6352",
|
| 133 | },
|
| 134 | .probe = hmc6352_probe,
|
| 135 | .remove = hmc6352_remove,
|
| 136 | .id_table = hmc6352_id,
|
| 137 | };
|
| 138 |
|
| 139 | module_i2c_driver(hmc6352_driver);
|
| 140 |
|
| 141 | MODULE_AUTHOR("Kalhan Trisal <[email protected]");
|
| 142 | MODULE_DESCRIPTION("hmc6352 Compass Driver");
|
| 143 | MODULE_LICENSE("GPL v2");
|