hmc6352.c (3530B)
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");