Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * Driver for AMSYS MS5611 barometer sensor chips
- *
- * MS5611:
- * High Precision Pressure Sensor Module with I2C Interface
- * Datasheet: http://www.amsys-sensor.com/products/ms5611.htm
- *
- * Copyright (C) 2014 Clément Guérin <clement.guerin@ciblenetworks.fr>
- * Copyright (C) 2012-2013 Olaf Lüke <olaf@tinkerforge.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- */
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/slab.h>
- #include <linux/jiffies.h>
- #include <linux/i2c.h>
- #include <linux/hwmon.h>
- #include <linux/hwmon-sysfs.h>
- #include <linux/err.h>
- #include <linux/mutex.h>
- #include <linux/delay.h>
- /* Command definitions */
- #define MS5611_CMD_RESET 0x1E
- #define MS5611_CMD_D1 0x40
- #define MS5611_CMD_D2 0x50
- #define MS5611_CMD_ADC_READ 0x00
- /* Over Sampling Ratio definitions */
- #define MS561101BA_OSR_256 0x00
- #define MS561101BA_OSR_512 0x02
- #define MS561101BA_OSR_1024 0x04
- #define MS561101BA_OSR_2048 0x06
- #define MS561101BA_OSR_4096 0x08
- #define MS561101BA_OSR MS561101BA_OSR_4096
- /* PROM definitions */
- #define MS5611_PROM_ADDR 0xA0
- #define MS5611_PROM_COUNT 8
- #define MS5611_PROM_SIZE 2
- enum ms5xxx_ids { ms5611 };
- struct ms5xxx_data {
- struct device *hwmon_dev;
- struct mutex update_lock;
- struct mutex adc_lock;
- u8 prom_valid;
- u16 prom[MS5611_PROM_COUNT];
- u32 d1, d2;
- s32 dt, temp, pres;
- unsigned long last_updated_d1;
- unsigned long last_updated_d2;
- };
- /* Send command */
- static struct ms5xxx_data *ms5xxx_cmd(struct device *dev, u8 cmd)
- {
- struct i2c_client *client = to_i2c_client(dev);
- struct ms5xxx_data *data = i2c_get_clientdata(client);
- mutex_lock(&data->update_lock);
- i2c_smbus_write_byte(client, cmd);
- mutex_unlock(&data->update_lock);
- return data;
- }
- /* Read calibration data */
- static struct ms5xxx_data *ms5xxx_read_prom(struct device *dev)
- {
- struct i2c_client *client = to_i2c_client(dev);
- struct ms5xxx_data *data = i2c_get_clientdata(client);
- u8 bytes[MS5611_PROM_SIZE];
- u8 i;
- mutex_lock(&data->update_lock);
- for (i = 0; i < MS5611_PROM_COUNT; i++) {
- u8 cmd = MS5611_PROM_ADDR + i * MS5611_PROM_SIZE;
- i2c_smbus_read_i2c_block_data(client, cmd, MS5611_PROM_SIZE, bytes);
- data->prom[i] = (bytes[0] << 8) | bytes[1];
- }
- mutex_unlock(&data->update_lock);
- return data;
- }
- /* Validate calibration data */
- static struct ms5xxx_data *ms5xxx_validate_prom(struct device *dev)
- {
- struct i2c_client *client = to_i2c_client(dev);
- struct ms5xxx_data *data = i2c_get_clientdata(client);
- u16 reminder = 0;
- u16 backup = data->prom[7];
- u8 i, k;
- data->prom[7] = 0xFF00 & data->prom[7];
- for (i = 0; i < 2 * MS5611_PROM_COUNT; i++) {
- if (i % 2 == 1) {
- reminder ^= data->prom[i >> 1] & 0x00FF;
- } else {
- reminder ^= data->prom[i >> 1] >> 8;
- }
- for (k = 8; k > 0; k--) {
- if (reminder & 0x8000) {
- reminder = (reminder << 1) ^ 0x3000;
- } else {
- reminder = reminder << 1;
- }
- }
- }
- reminder = 0x000F & (reminder >> 12);
- data->prom[7] = backup;
- data->prom_valid = (reminder == (data->prom[7] & 0x000F));
- return data;
- }
- /* Read sensor data */
- static struct ms5xxx_data *ms5xxx_read_adc(struct device *dev, char n)
- {
- struct i2c_client *client = to_i2c_client(dev);
- struct ms5xxx_data *data = i2c_get_clientdata(client);
- u8 bytes[3];
- mutex_lock(&data->update_lock);
- i2c_smbus_read_i2c_block_data(client, MS5611_CMD_ADC_READ, 3, bytes);
- if (n == 0)
- data->d1 = (bytes[0] << 16) | (bytes[1] << 8) | bytes[2];
- else
- data->d2 = (bytes[0] << 16) | (bytes[1] << 8) | bytes[2];
- mutex_unlock(&data->update_lock);
- return data;
- }
- /* Update pressure data */
- static struct ms5xxx_data *ms5xxx_update_pres(struct device *dev)
- {
- struct i2c_client *client = to_i2c_client(dev);
- struct ms5xxx_data *data = i2c_get_clientdata(client);
- s64 off, sens;
- if (time_after(jiffies, data->last_updated_d1 + HZ / 2)) {
- /* Read D1 */
- mutex_lock(&data->adc_lock);
- ms5xxx_cmd(dev, MS5611_CMD_D1 | MS561101BA_OSR);
- msleep(10);
- ms5xxx_read_adc(dev, 0);
- mutex_unlock(&data->adc_lock);
- /* Calculate pressure */
- off = ((s64)data->prom[2] << 16) + (((s64)data->prom[4] * (s64)data->dt) >> 7);
- sens = ((s64)data->prom[1] << 15) + (((s64)data->prom[3] * (s64)data->dt) >> 8);
- if (data->temp < 2000) {
- s32 k = 5 * (data->temp - 2000) * (data->temp - 2000);
- off -= k >> 1;
- sens -= k >> 2;
- if (data->temp < -1500) {
- s32 n = (data->temp + 1500) * (data->temp + 1500);
- off -= 7 * n;
- sens -= (11 * n) >> 1;
- }
- }
- data->pres = (((((int64_t)data->d1 * sens) >> 21) - off) * 10) >> 15;
- data->last_updated_d1 = jiffies;
- }
- return data;
- }
- /* Update temperature data */
- static struct ms5xxx_data *ms5xxx_update_temp(struct device *dev)
- {
- struct i2c_client *client = to_i2c_client(dev);
- struct ms5xxx_data *data = i2c_get_clientdata(client);
- if (time_after(jiffies, data->last_updated_d2 + HZ / 2)) {
- /* Read D2 */
- mutex_lock(&data->adc_lock);
- ms5xxx_cmd(dev, MS5611_CMD_D2 | MS561101BA_OSR);
- msleep(10);
- ms5xxx_read_adc(dev, 1);
- mutex_unlock(&data->adc_lock);
- /* Calculate temperature */
- data->dt = (s32)data->d2 - ((s32)data->prom[5] << 8);
- data->temp = 2000 + (((s64)data->dt * (s64)data->prom[6]) >> 23);
- if (data->temp < 2000) {
- data->temp -= ((s64)data->dt * (s64)data->dt) >> 31;
- }
- data->last_updated_d2 = jiffies;
- }
- return data;
- }
- static ssize_t show_prom(struct device *dev, struct device_attribute *da,
- char *buf)
- {
- struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
- struct i2c_client *client = to_i2c_client(dev);
- struct ms5xxx_data *data = i2c_get_clientdata(client);
- return sprintf(buf, "%d\n", data->prom[attr->index]);
- }
- static ssize_t show_prom_valid(struct device *dev, struct device_attribute *da,
- char *buf)
- {
- struct i2c_client *client = to_i2c_client(dev);
- struct ms5xxx_data *data = i2c_get_clientdata(client);
- return sprintf(buf, "%d\n", data->prom_valid);
- }
- static ssize_t show_pres(struct device *dev, struct device_attribute *da,
- char *buf)
- {
- struct ms5xxx_data *data = ms5xxx_update_temp(dev);
- ms5xxx_update_pres(dev);
- return sprintf(buf, "%d\n", data->pres);
- }
- static ssize_t show_temp(struct device *dev, struct device_attribute *da,
- char *buf)
- {
- struct ms5xxx_data *data = ms5xxx_update_temp(dev);
- return sprintf(buf, "%d\n", data->temp);
- }
- static SENSOR_DEVICE_ATTR(prom0, S_IRUGO, show_prom, NULL, 0);
- static SENSOR_DEVICE_ATTR(prom1, S_IRUGO, show_prom, NULL, 1);
- static SENSOR_DEVICE_ATTR(prom2, S_IRUGO, show_prom, NULL, 2);
- static SENSOR_DEVICE_ATTR(prom3, S_IRUGO, show_prom, NULL, 3);
- static SENSOR_DEVICE_ATTR(prom4, S_IRUGO, show_prom, NULL, 4);
- static SENSOR_DEVICE_ATTR(prom5, S_IRUGO, show_prom, NULL, 5);
- static SENSOR_DEVICE_ATTR(prom6, S_IRUGO, show_prom, NULL, 6);
- static SENSOR_DEVICE_ATTR(prom7, S_IRUGO, show_prom, NULL, 7);
- static SENSOR_DEVICE_ATTR(prom_valid, S_IRUGO, show_prom_valid, NULL, 0);
- static SENSOR_DEVICE_ATTR(pres, S_IRUGO, show_pres, NULL, 0);
- static SENSOR_DEVICE_ATTR(temp, S_IRUGO, show_temp, NULL, 0);
- static struct attribute *ms5xxx_attributes[] = {
- &sensor_dev_attr_prom0.dev_attr.attr,
- &sensor_dev_attr_prom1.dev_attr.attr,
- &sensor_dev_attr_prom2.dev_attr.attr,
- &sensor_dev_attr_prom3.dev_attr.attr,
- &sensor_dev_attr_prom4.dev_attr.attr,
- &sensor_dev_attr_prom5.dev_attr.attr,
- &sensor_dev_attr_prom6.dev_attr.attr,
- &sensor_dev_attr_prom7.dev_attr.attr,
- &sensor_dev_attr_prom_valid.dev_attr.attr,
- &sensor_dev_attr_pres.dev_attr.attr,
- &sensor_dev_attr_temp.dev_attr.attr,
- NULL
- };
- static const struct attribute_group ms5xxx_group = {
- .attrs = ms5xxx_attributes,
- };
- static int ms5xxx_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
- {
- struct ms5xxx_data *data;
- int err;
- data = kzalloc(sizeof(struct ms5xxx_data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- goto exit;
- }
- i2c_set_clientdata(client, data);
- mutex_init(&data->update_lock);
- mutex_init(&data->adc_lock);
- /* Register sysfs hooks */
- err = sysfs_create_group(&client->dev.kobj, &ms5xxx_group);
- if (err)
- goto exit_free;
- data->hwmon_dev = hwmon_device_register(&client->dev);
- if (IS_ERR(data->hwmon_dev)) {
- err = PTR_ERR(data->hwmon_dev);
- goto exit_remove;
- }
- ms5xxx_cmd(&client->dev, MS5611_CMD_RESET);
- msleep(5);
- ms5xxx_read_prom(&client->dev);
- ms5xxx_validate_prom(&client->dev);
- return 0;
- exit_remove:
- sysfs_remove_group(&client->dev.kobj, &ms5xxx_group);
- exit_free:
- kfree(data);
- exit:
- return err;
- }
- static int ms5xxx_remove(struct i2c_client *client)
- {
- struct ms5xxx_data *data = i2c_get_clientdata(client);
- hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&client->dev.kobj, &ms5xxx_group);
- kfree(data);
- return 0;
- }
- static const struct i2c_device_id ms5xxx_id[] = {
- { "ms5611", ms5611 },
- { }
- };
- MODULE_DEVICE_TABLE(i2c, ms5xxx_id);
- static struct i2c_driver ms5xxx_driver = {
- .driver = {
- .name = "ms5xxx",
- },
- .probe = ms5xxx_probe,
- .remove = ms5xxx_remove,
- .id_table = ms5xxx_id,
- };
- module_i2c_driver(ms5xxx_driver);
- MODULE_AUTHOR("Clément Guérin <clement.guerin@ciblenetworks.fr>");
- MODULE_DESCRIPTION("ms5xxx driver");
- MODULE_LICENSE("GPL");
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement