1cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal/*
2cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal * hmc6352.c - Honeywell Compass Driver
3cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal *
4cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal * Copyright (C) 2009 Intel Corp
5cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal *
6cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal *
8cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal * This program is free software; you can redistribute it and/or modify
9cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal * it under the terms of the GNU General Public License as published by
10cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal * the Free Software Foundation; version 2 of the License.
11cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal *
12cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal * This program is distributed in the hope that it will be useful, but
13cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal * WITHOUT ANY WARRANTY; without even the implied warranty of
14cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal * General Public License for more details.
16cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal *
17cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal * You should have received a copy of the GNU General Public License along
18cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal * with this program; if not, write to the Free Software Foundation, Inc.,
19cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
20cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
21cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal *
22cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal */
23cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal
24cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal#include <linux/module.h>
25cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal#include <linux/slab.h>
26cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal#include <linux/i2c.h>
27cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal#include <linux/err.h>
28cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal#include <linux/delay.h>
29cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal#include <linux/sysfs.h>
30cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal
31cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisalstatic DEFINE_MUTEX(compass_mutex);
32cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal
33cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisalstatic int compass_command(struct i2c_client *c, u8 cmd)
34cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal{
35cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	int ret = i2c_master_send(c, &cmd, 1);
36cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	if (ret < 0)
37cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal		dev_warn(&c->dev, "command '%c' failed.\n", cmd);
38cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	return ret;
39cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal}
40cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal
41cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisalstatic int compass_store(struct device *dev, const char *buf, size_t count,
42cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal			const char *map)
43cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal{
44cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	struct i2c_client *c = to_i2c_client(dev);
45cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	int ret;
46cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	unsigned long val;
47cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal
48f7b41276b6b07f47c5f5212fa244385b0e3aaa30Jingoo Han	ret = kstrtoul(buf, 10, &val);
49f7b41276b6b07f47c5f5212fa244385b0e3aaa30Jingoo Han	if (ret)
50f7b41276b6b07f47c5f5212fa244385b0e3aaa30Jingoo Han		return ret;
51cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	if (val >= strlen(map))
52cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal		return -EINVAL;
53cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	mutex_lock(&compass_mutex);
54cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	ret = compass_command(c, map[val]);
55cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	mutex_unlock(&compass_mutex);
56cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	if (ret < 0)
57cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal		return ret;
58cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	return count;
59cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal}
60cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal
61cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisalstatic ssize_t compass_calibration_store(struct device *dev,
62cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal		struct device_attribute *attr, const char *buf, size_t count)
63cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal{
64cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	return compass_store(dev, buf, count, "EC");
65cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal}
66cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal
67cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisalstatic ssize_t compass_power_mode_store(struct device *dev,
68cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal		struct device_attribute *attr, const  char *buf, size_t count)
69cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal{
70cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	return compass_store(dev, buf, count, "SW");
71cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal}
72cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal
73cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisalstatic ssize_t compass_heading_data_show(struct device *dev,
74cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal			struct device_attribute *attr, char *buf)
75cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal{
76cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	struct i2c_client *client = to_i2c_client(dev);
77cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	unsigned char i2c_data[2];
786f7d485e13c6c07348cf9cfd1b735fe1bcf3caa9Axel Lin	int ret;
79cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal
80cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	mutex_lock(&compass_mutex);
81cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	ret = compass_command(client, 'A');
82cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	if (ret != 1) {
83cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal		mutex_unlock(&compass_mutex);
84cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal		return ret;
85cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	}
86cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	msleep(10); /* sending 'A' cmd we need to wait for 7-10 millisecs */
87cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	ret = i2c_master_recv(client, i2c_data, 2);
88cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	mutex_unlock(&compass_mutex);
896f7d485e13c6c07348cf9cfd1b735fe1bcf3caa9Axel Lin	if (ret < 0) {
90cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal		dev_warn(dev, "i2c read data cmd failed\n");
91cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal		return ret;
92cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	}
93cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	ret = (i2c_data[0] << 8) | i2c_data[1];
94cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	return sprintf(buf, "%d.%d\n", ret/10, ret%10);
95cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal}
96cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal
97cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal
98cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisalstatic DEVICE_ATTR(heading0_input, S_IRUGO, compass_heading_data_show, NULL);
99cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisalstatic DEVICE_ATTR(calibration, S_IWUSR, NULL, compass_calibration_store);
100cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisalstatic DEVICE_ATTR(power_state, S_IWUSR, NULL, compass_power_mode_store);
101cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal
102cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisalstatic struct attribute *mid_att_compass[] = {
103cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	&dev_attr_heading0_input.attr,
104cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	&dev_attr_calibration.attr,
105cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	&dev_attr_power_state.attr,
106cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	NULL
107cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal};
108cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal
109cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisalstatic const struct attribute_group m_compass_gr = {
110cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	.name = "hmc6352",
111cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	.attrs = mid_att_compass
112cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal};
113cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal
114cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisalstatic int hmc6352_probe(struct i2c_client *client,
115cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal					const struct i2c_device_id *id)
116cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal{
117cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	int res;
118cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal
119cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	res = sysfs_create_group(&client->dev.kobj, &m_compass_gr);
120cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	if (res) {
121cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal		dev_err(&client->dev, "device_create_file failed\n");
122cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal		return res;
123cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	}
124cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	dev_info(&client->dev, "%s HMC6352 compass chip found\n",
125cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal							client->name);
126cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	return 0;
127cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal}
128cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal
129cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisalstatic int hmc6352_remove(struct i2c_client *client)
130cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal{
131cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	sysfs_remove_group(&client->dev.kobj, &m_compass_gr);
132cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	return 0;
133cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal}
134cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal
135cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisalstatic struct i2c_device_id hmc6352_id[] = {
136cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	{ "hmc6352", 0 },
137cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	{ }
138cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal};
139cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal
140cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan TrisalMODULE_DEVICE_TABLE(i2c, hmc6352_id);
141cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal
142cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisalstatic struct i2c_driver hmc6352_driver = {
143cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	.driver = {
144cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal		.name = "hmc6352",
145cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	},
146cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	.probe = hmc6352_probe,
147cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	.remove = hmc6352_remove,
148cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal	.id_table = hmc6352_id,
149cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal};
150cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal
151a64fe2ed76614d37abb6966a67f4f39d10efba3cAxel Linmodule_i2c_driver(hmc6352_driver);
152cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan Trisal
153cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan TrisalMODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com");
154cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan TrisalMODULE_DESCRIPTION("hmc6352 Compass Driver");
155cfa3b24c38b58cb6acabe6441b4668e530e957afKalhan TrisalMODULE_LICENSE("GPL v2");
156