From eb0bb7eec1525b63cd005ce03474025c918beade Mon Sep 17 00:00:00 2001 From: sathya Date: Fri, 4 Nov 2011 14:32:53 -0700 Subject: [PATCH] Support for baro sensor: fork lifted ms5607 driver from git://jfumg-gcrmirror.jf.intel.com/a/bsp/hardware/intel/linux-2.6.git Change-Id: If05236ef565fbd51641ea322bdcc810cf52d3034 Reviewed-on: http://android.intel.com:8080/23344 Reviewed-by: Gross, Mark Tested-by: Gross, Mark --- arch/x86/platform/mfld/blackbay_pr2.c | 13 + drivers/hwmon/Kconfig | 8 + drivers/hwmon/Makefile | 1 + drivers/hwmon/ms5607.c | 581 ++++++++++++++++++++++++++++++++++ include/linux/ms5607.h | 35 ++ 5 files changed, 638 insertions(+) create mode 100644 drivers/hwmon/ms5607.c create mode 100644 include/linux/ms5607.h diff --git a/arch/x86/platform/mfld/blackbay_pr2.c b/arch/x86/platform/mfld/blackbay_pr2.c index a2cc34f..085a89c 100644 --- a/arch/x86/platform/mfld/blackbay_pr2.c +++ b/arch/x86/platform/mfld/blackbay_pr2.c @@ -19,6 +19,7 @@ #include #include #include +#include #include static u8 mxt_valid_interrupt(void) @@ -75,6 +76,12 @@ static struct lis3dh_acc_platform_data lis3dh_pdata = { .gpio_int1 = 60, .gpio_int2 = 61, }; + +static struct ms5607_platform_data baro_pdata = { + .poll_interval = 100, + .min_interval = 0, +}; + #define MPU_GPIO_PIN 56 static struct i2c_board_info pr2_i2c_bus5_devs[] = { @@ -94,6 +101,12 @@ static struct i2c_board_info pr2_i2c_bus5_devs[] = { .irq = MPU_GPIO_PIN, .addr = 0x68, }, + { + .type = "baro", + .irq = 0xff, + .addr = 0x77, + .platform_data = &baro_pdata, + }, }; static struct gpio_keys_button gpio_button[] = { diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index d9115cc..6be4d7d 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1387,6 +1387,14 @@ config SENSORS_MPU3050 please search on http://www.invensense.com/ for product details. +config SENSORS_MS5607 + tristate "Measurement MS5607 digital pressure sensor" + depends on I2C && SYSFS + help + If you say yes here you get support for the Measurement MS5607 digital + pressure sensor. + + config MSIC_GPADC tristate "MSIC GPADC driver for Intel Medfield platform" depends on INTEL_SCU_IPC diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 7d11ae6..df9b20e 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -121,6 +121,7 @@ obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o obj-$(CONFIG_SENSORS_LIS3DH_ACC) += lis3dh_acc.o obj-$(CONFIG_SENSORS_HMC5883) += hmc5883.o obj-$(CONFIG_SENSORS_MPU3050) += mpu3050.o +obj-$(CONFIG_SENSORS_MS5607) += ms5607.o obj-$(CONFIG_MSIC_GPADC) += intel_mid_gpadc.o # PMBus drivers diff --git a/drivers/hwmon/ms5607.c b/drivers/hwmon/ms5607.c new file mode 100644 index 0000000..fca0f93 --- /dev/null +++ b/drivers/hwmon/ms5607.c @@ -0,0 +1,581 @@ +/* + * Copyright (C) 2011 Seraphim, Inc. + * Written by Frank Liao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NAME "baro" + +#define CMD_RESET 0x1E /* ADC reset command */ +#define CMD_ADC_READ 0x00 /* ADC read command */ +#define CMD_ADC_CONV 0x40 /* ADC conversion command */ +#define CMD_ADC_D1 0x00 /* ADC D1 conversion */ +#define CMD_ADC_D2 0x10 /* ADC D2 conversion */ +#define CMD_ADC_256 0x00 /* ADC OSR=256 */ +#define CMD_ADC_512 0x02 /* ADC OSR=512 */ +#define CMD_ADC_1024 0x04 /* ADC OSR=1024 */ +#define CMD_ADC_2048 0x06 /* ADC OSR=2048 */ +#define CMD_ADC_4096 0x08 /* ADC OSR=4096 */ +#define CMD_PROM_RD 0xA0 /* Prom read command */ + +#define MS5607_COEFF_DATA_LEN 8 + +#define MS5607_MIN_DELAY 50 +#define MS5607_DEFAULT_DELAY 200 + +struct ms5607_data { + struct i2c_client *client; + struct ms5607_platform_data *pdata; + struct mutex lock; + + struct delayed_work input_work; + struct workqueue_struct *workqueue; + + struct input_dev *input_dev; + struct early_suspend es; + + int enabled; + bool powered; + bool report_coeff; + int delay_ms; + + unsigned int C[MS5607_COEFF_DATA_LEN]; /* calibration coefficients */ +}; + +/* + * send reset sequence + */ +void cmd_reset(struct i2c_client *client) +{ + u8 buf[7]; + + buf[0] = 0x01; + if (i2c_smbus_write_i2c_block_data(client, CMD_RESET, 0, buf) < 0) + return; + /* + * Reset sequence requires 2.8ms to allow the calibration + * PROM to be loaded into the internal register + */ + usleep_range(3000, 4000); +} + +/* + * read calibration coefficients + */ +unsigned int cmd_prom(struct i2c_client *client, char coef_num) +{ + int err; + u8 buf[7]; + unsigned int calib_coeff; + + err = i2c_smbus_read_i2c_block_data(client, CMD_PROM_RD + coef_num * 2, + 2, buf); + if (err < 0) + return err; + calib_coeff = 256 * buf[0]; + calib_coeff += buf[1]; + return calib_coeff; +} + +/* + * preform adc conversion + */ +unsigned long cmd_adc(struct ms5607_data *ms5607, char cmd) +{ + int err; + u8 buf[7]; + unsigned long temp; + int delay; + + err = i2c_smbus_write_i2c_block_data(ms5607->client, + CMD_ADC_CONV + cmd, 0, buf); + if (err < 0) + return err; + + switch (cmd & 0x0f) { /* wait necessary conversion time */ + case CMD_ADC_256: + delay = 1000; + break; + case CMD_ADC_512: + delay = 3000; + break; + case CMD_ADC_1024: + delay = 4000; + break; + case CMD_ADC_2048: + delay = 6000; + break; + case CMD_ADC_4096: + delay = 10000; + break; + default: + delay = 1000; + break; + } + usleep_range(delay, delay + 1000); + + err = i2c_smbus_read_i2c_block_data(ms5607->client, + CMD_ADC_READ, 3, buf); + if (err < 0) + return err; + + temp = 65536 * buf[0]; + temp += 256 * buf[1]; + temp += buf[2]; + return temp; +} + +static void ms5607_read_coeff(struct ms5607_data *ms5607) +{ + unsigned char i; + + for (i = 0; i < MS5607_COEFF_DATA_LEN; i++) + ms5607->C[i] = cmd_prom(ms5607->client, i); +} + +static void ms5607_power_off(struct ms5607_data *ms5607) +{ + if (ms5607->pdata->power_off) + ms5607->pdata->power_off(); +} + +static int ms5607_power_on(struct ms5607_data *ms5607) +{ + int err; + + if (ms5607->pdata->power_on) { + err = ms5607->pdata->power_on(); + if (err < 0) + return err; + } + + msleep(100); + cmd_reset(ms5607->client); + return 0; +} + +static void ms5607_get_data(struct ms5607_data *ms5607, + unsigned long *data) +{ + /* ADC value of the pressure conversion - read D1 */ + data[0] = cmd_adc(ms5607, CMD_ADC_D1 + CMD_ADC_4096); + /* ADC value of the temperature conversion - read D2 */ + data[1] = cmd_adc(ms5607, CMD_ADC_D2 + CMD_ADC_4096); +} + +static void ms5607_report_values(struct ms5607_data *ms5607, + unsigned long *data) +{ + input_report_rel(ms5607->input_dev, REL_X, data[0]); + input_report_rel(ms5607->input_dev, REL_Y, data[1]); + + if (ms5607->report_coeff) { + input_report_abs(ms5607->input_dev, ABS_HAT0X, ms5607->C[0]); + input_report_abs(ms5607->input_dev, ABS_HAT0Y, ms5607->C[1]); + input_report_abs(ms5607->input_dev, ABS_HAT1X, ms5607->C[2]); + input_report_abs(ms5607->input_dev, ABS_HAT1Y, ms5607->C[3]); + input_report_abs(ms5607->input_dev, ABS_HAT2X, ms5607->C[4]); + input_report_abs(ms5607->input_dev, ABS_HAT2Y, ms5607->C[5]); + input_report_abs(ms5607->input_dev, ABS_HAT3X, ms5607->C[6]); + input_report_abs(ms5607->input_dev, ABS_HAT3Y, ms5607->C[7]); + + ms5607->report_coeff = false; + } + + input_sync(ms5607->input_dev); +} + +static void ms5607_enable(struct ms5607_data *ms5607) +{ + if (!ms5607->powered) { + ms5607_power_on(ms5607); + ms5607->powered = true; + + queue_delayed_work(ms5607->workqueue, &ms5607->input_work, + msecs_to_jiffies(ms5607->delay_ms)); + } +} + +static void ms5607_disable(struct ms5607_data *ms5607) +{ + if (ms5607->powered) { + cancel_delayed_work_sync(&ms5607->input_work); + ms5607_power_off(ms5607); + ms5607->powered = false; + } +} + +static void ms5607_input_work_func(struct work_struct *work) +{ + struct ms5607_data *ms5607; + unsigned long pt[2] = { 0 }; + + ms5607 = container_of((struct delayed_work *)work, struct ms5607_data, + input_work); + + ms5607_get_data(ms5607, pt); + ms5607_report_values(ms5607, pt); + + queue_delayed_work(ms5607->workqueue, &ms5607->input_work, + msecs_to_jiffies(ms5607->delay_ms)); +} + +static int ms5607_input_init(struct ms5607_data *ms5607) +{ + int err; + + INIT_DELAYED_WORK(&ms5607->input_work, ms5607_input_work_func); + ms5607->input_dev = input_allocate_device(); + if (!ms5607->input_dev) { + dev_err(&ms5607->client->dev, "input device allocate failed\n"); + return -ENOMEM; + } + + ms5607->input_dev->name = "ms5607_pressure"; + ms5607->input_dev->id.bustype = BUS_I2C; + ms5607->input_dev->dev.parent = &ms5607->client->dev; + input_set_drvdata(ms5607->input_dev, ms5607); + + set_bit(EV_ABS, ms5607->input_dev->evbit); + set_bit(EV_REL, ms5607->input_dev->evbit); + set_bit(ABS_MISC, ms5607->input_dev->absbit); + + set_bit(REL_X, ms5607->input_dev->relbit); + set_bit(REL_Y, ms5607->input_dev->relbit); + input_set_abs_params(ms5607->input_dev, ABS_HAT0X, 0, 65535, 0, 0); + input_set_abs_params(ms5607->input_dev, ABS_HAT0Y, 0, 65535, 0, 0); + input_set_abs_params(ms5607->input_dev, ABS_HAT1X, 0, 65535, 0, 0); + input_set_abs_params(ms5607->input_dev, ABS_HAT1Y, 0, 65535, 0, 0); + input_set_abs_params(ms5607->input_dev, ABS_HAT2X, 0, 65535, 0, 0); + input_set_abs_params(ms5607->input_dev, ABS_HAT2Y, 0, 65535, 0, 0); + input_set_abs_params(ms5607->input_dev, ABS_HAT3X, 0, 65535, 0, 0); + input_set_abs_params(ms5607->input_dev, ABS_HAT3Y, 0, 65535, 0, 0); + + err = input_register_device(ms5607->input_dev); + if (err) { + dev_err(&ms5607->client->dev, + "unable to register input polled device %s: %d\n", + ms5607->input_dev->name, err); + goto err1; + } + + return 0; +err1: + input_free_device(ms5607->input_dev); + return err; +} + +/* sysfs */ +static ssize_t ms5607_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ms5607_data *ms5607 = i2c_get_clientdata(client); + + return sprintf(buf, "%d\n", ms5607->delay_ms); +} + +static ssize_t ms5607_delay_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long pollms; + struct i2c_client *client = to_i2c_client(dev); + struct ms5607_data *ms5607 = i2c_get_clientdata(client); + + if (strict_strtoul(buf, 10, &pollms)) + return -EINVAL; + + pollms = pollms > MS5607_MIN_DELAY ? pollms : MS5607_MIN_DELAY; + ms5607->delay_ms = pollms; + return count; +} + +static ssize_t ms5607_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ms5607_data *ms5607 = i2c_get_clientdata(client); + + return sprintf(buf, "%d\n", ms5607->enabled); +} + +#define MS5607_SYSFS_POWERON 1 +#define MS5607_SYSFS_POWERDOWN 0 + +static ssize_t ms5607_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ms5607_data *ms5607 = i2c_get_clientdata(client); + unsigned long val; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + if (val != MS5607_SYSFS_POWERON && val != MS5607_SYSFS_POWERDOWN) + return -EINVAL; + + mutex_lock(&ms5607->lock); + if (val) + ms5607_enable(ms5607); + else + ms5607_disable(ms5607); + + ms5607->enabled = val; + /* + * We don't know if user space application has e.g. restarted. So + * better to report coeff data again. + */ + + ms5607->report_coeff = val; + mutex_unlock(&ms5607->lock); + + return count; +} + +static ssize_t ms5607_coeff_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ms5607_data *ms5607 = i2c_get_clientdata(client); + + return sprintf(buf, "C0: %d\nC1: %d\nC2: %d\nC3: %d\nC4: %d\nC5: %d\n"\ + "C6: %d\nC7: %d\n", ms5607->C[0], ms5607->C[0], + ms5607->C[1], ms5607->C[2], ms5607->C[3], ms5607->C[4], + ms5607->C[5], ms5607->C[6], ms5607->C[7]); +} + +static DEVICE_ATTR(coeff, S_IRUGO, ms5607_coeff_show, NULL); +static DEVICE_ATTR(poll, S_IRUGO|S_IWUSR, ms5607_delay_show, + ms5607_delay_store); +static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR, ms5607_enable_show, + ms5607_enable_store); + +static struct attribute *ms5607_attributes[] = { + &dev_attr_coeff.attr, + &dev_attr_poll.attr, + &dev_attr_enable.attr, + NULL +}; + +static struct attribute_group ms5607_attribute_group = { + .attrs = ms5607_attributes +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void ms5607_early_suspend(struct early_suspend *h) +{ + struct ms5607_data *ms5607 = container_of(h, struct ms5607_data, es); + + mutex_lock(&ms5607->lock); + if (ms5607->enabled) + ms5607_disable(ms5607); + mutex_unlock(&ms5607->lock); +} +static void ms5607_late_resume(struct early_suspend *h) +{ + struct ms5607_data *ms5607 = container_of(h, struct ms5607_data, es); + + mutex_lock(&ms5607->lock); + if (ms5607->enabled) + ms5607_enable(ms5607); + mutex_unlock(&ms5607->lock); +} +#endif + +static int __devinit ms5607_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err; + struct ms5607_data *ms5607; + + if (client->dev.platform_data == NULL) { + dev_err(&client->dev, "platform data is NULL\n"); + return -ENODEV; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_I2C | I2C_FUNC_SMBUS_I2C_BLOCK)) { + dev_err(&client->dev, "client not i2c capable\n"); + return -ENODEV; + } + + ms5607 = kzalloc(sizeof(*ms5607), GFP_KERNEL); + if (!ms5607) { + dev_err(&client->dev, + "failed to allocate memory for module data\n"); + return -ENOMEM; + } + + mutex_init(&ms5607->lock); + ms5607->client = client; + i2c_set_clientdata(client, ms5607); + + ms5607->pdata = kmalloc(sizeof(*ms5607->pdata), GFP_KERNEL); + if (!ms5607->pdata) { + dev_err(&client->dev, "insufficient memory\n"); + err = -ENOMEM; + goto err_alloc_pdata; + } + + memcpy(ms5607->pdata, client->dev.platform_data, + sizeof(*ms5607->pdata)); + if (ms5607->pdata->init) { + err = ms5607->pdata->init(); + if (err < 0) { + dev_err(&client->dev, "init error\n"); + goto err_init; + } + } + + ms5607->delay_ms = MS5607_DEFAULT_DELAY; + + ms5607->workqueue = create_singlethread_workqueue("ms5607"); + if (ms5607->workqueue == NULL) { + dev_err(&client->dev, "couldn't create workqueue\n"); + err = -ENOMEM; + goto err_create_qw; + } + + err = ms5607_input_init(ms5607); + if (err < 0) { + dev_err(&client->dev, "input init error\n"); + goto err_input_init; + } + + /* Read coefficiency data */ + ms5607_power_on(ms5607); + ms5607_read_coeff(ms5607); + + ms5607_power_off(ms5607); + ms5607->enabled = 0; + +#ifdef CONFIG_HAS_EARLYSUSPEND + ms5607->es.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 10; + ms5607->es.suspend = ms5607_early_suspend; + ms5607->es.resume = ms5607_late_resume; + register_early_suspend(&ms5607->es); +#endif + + err = sysfs_create_group(&client->dev.kobj, &ms5607_attribute_group); + if (err) { + dev_err(&client->dev, "sysfs can not create group\n"); + goto err_sysfs; + } + + return 0; + +err_sysfs: +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ms5607->es); +#endif + input_unregister_device(ms5607->input_dev); +err_input_init: + destroy_workqueue(ms5607->workqueue); +err_create_qw: + if (ms5607->pdata->exit) + ms5607->pdata->exit(); +err_init: + kfree(ms5607->pdata); +err_alloc_pdata: + kfree(ms5607); + return err; +} + +static int __devexit ms5607_remove(struct i2c_client *client) +{ + struct ms5607_data *ms5607 = i2c_get_clientdata(client); + + input_unregister_device(ms5607->input_dev); + + flush_workqueue(ms5607->workqueue); + destroy_workqueue(ms5607->workqueue); + +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ms5607->es); +#endif + ms5607_power_off(ms5607); + if (ms5607->pdata->exit) + ms5607->pdata->exit(); + kfree(ms5607->pdata); + sysfs_remove_group(&client->dev.kobj, &ms5607_attribute_group); + kfree(ms5607); + + return 0; +} + +#ifdef CONFIG_PM +static int ms5607_resume(struct device *dev) +{ + return 0; +} + +static int ms5607_suspend(struct device *dev) +{ + return 0; +} + +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops ms5607_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ms5607_suspend, + ms5607_resume) +}; + +static const struct i2c_device_id ms5607_id[] = { + {NAME, 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, ms5607_id); + +static struct i2c_driver ms5607_driver = { + .driver = { + .name = NAME, + .owner = THIS_MODULE, + .pm = &ms5607_pm_ops, + }, + .probe = ms5607_probe, + .remove = __devexit_p(ms5607_remove), + .id_table = ms5607_id, +}; + +static int __init ms5607_init(void) +{ + return i2c_add_driver(&ms5607_driver); +} + +static void __exit ms5607_exit(void) +{ + i2c_del_driver(&ms5607_driver); +} + +module_init(ms5607_init); +module_exit(ms5607_exit); + +MODULE_DESCRIPTION("ms5607 pressure sensor driver"); +MODULE_AUTHOR("Frank Liao "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/ms5607.h b/include/linux/ms5607.h new file mode 100644 index 0000000..c5f909b --- /dev/null +++ b/include/linux/ms5607.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2009, Kionix, Inc. All Rights Reserved. + * Written by Chris Hudson + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef __MS5607_H__ +#define __MS5607_H__ + +#ifdef __KERNEL__ +struct ms5607_platform_data { + int poll_interval; + int min_interval; + + int (*init)(void); + void (*exit)(void); + int (*power_on)(void); + int (*power_off)(void); +}; +#endif /* __KERNEL__ */ + +#endif /* __MS5607_H__ */ -- 2.7.4